diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 45b15440c0..9ccfc8b9ed 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,16 +3,17 @@ In the description field of this PR, include "resolves #XXXX" tagging the issue you are fixing. If this PR addresses the issue but doesn't completely resolve it (ie the issue should remain open after your PR is merged), write "addresses #XXXX".--> + Resolves #[Add issue number here] - Changes: +Changes: - - Screenshots of the change: +Screenshots of the change: #### PR Checklist + diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000000..e040357e05 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,19 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "ignorePatterns": [ + "test/manual-test-examples/*", + "lib/*", + ".all-contributorsrc", + "test/mockServiceWorker.js", + "contributor_docs/project_wrapups/*", + "*.json", + "*.yml" + ], + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma":"none", + "arrowParens": "avoid" +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000000..3ce7393ab2 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,1402 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [], + "categories": { + "correctness": "off" + }, + "env": { + "builtin": true + }, + "ignorePatterns": [ + "!**/*.gitkeep", + ".nyc_output/*", + "**/local.code-workspace", + "node_modules/*", + "**/analyzer/", + "**/bower-repo/", + "**/coverage/", + "examples/3d/", + "experiments/*", + "**/dist/", + "docs/reference/*", + "docs/data.json", + "lib_old/*", + "lib/p5.*", + "lib/p5-test.js", + "lib/modules", + "**/p5-website/", + "**/preview/", + "**/release/", + "**/__screenshots__/", + "**/*.d.ts", + "**/p5.zip", + "**/yarn.lock", + "**/actual-screenshots/", + "**/visual-report.html", + "**/todo.md", + "**/*.DS_Store", + "**/.idea", + "**/.project", + "node_modules/**", + ".github/**", + ".vscode/**", + "contributor_docs/archive/**", + "contributor_docs/images/**", + "dist/**", + "lib/**", + "src/core/reference.js", + "src/type/lib/Typr.js", + "test/**/*", + "!test/unit/", + "!test/unit/**/*", + "translations/**", + "types/**", + "utils/sample-linter.mjs" + ], + "overrides": [ + { + "files": [ + "**/*.js", + "**/*.mjs" + ], + "rules": { + "constructor-super": "warn", + "for-direction": "warn", + "getter-return": "warn", + "no-async-promise-executor": "off", + "no-case-declarations": "warn", + "no-class-assign": "warn", + "no-compare-neg-zero": "warn", + "no-cond-assign": [ + "warn", + "except-parens" + ], + "no-const-assign": "warn", + "no-constant-binary-expression": "warn", + "no-constant-condition": "warn", + "no-control-regex": "warn", + "no-debugger": "warn", + "no-delete-var": "warn", + "no-dupe-class-members": "warn", + "no-dupe-else-if": "warn", + "no-dupe-keys": "warn", + "no-duplicate-case": "warn", + "no-empty": [ + "warn", + { + "allowEmptyCatch": true + } + ], + "no-empty-character-class": "warn", + "no-empty-pattern": "warn", + "no-empty-static-block": "warn", + "no-ex-assign": "warn", + "no-extra-boolean-cast": "warn", + "no-fallthrough": "warn", + "no-func-assign": "warn", + "no-global-assign": "warn", + "no-import-assign": "warn", + "no-invalid-regexp": "warn", + "no-irregular-whitespace": "warn", + "no-loss-of-precision": "warn", + "no-misleading-character-class": "warn", + "no-new-native-nonconstructor": "warn", + "no-nonoctal-decimal-escape": "warn", + "no-obj-calls": "warn", + "no-prototype-builtins": "off", + "no-redeclare": "warn", + "no-regex-spaces": "warn", + "no-self-assign": "warn", + "no-setter-return": "warn", + "no-shadow-restricted-names": "warn", + "no-sparse-arrays": "warn", + "no-this-before-super": "warn", + "no-unexpected-multiline": "warn", + "no-unreachable": "warn", + "no-unsafe-finally": "warn", + "no-unsafe-negation": "warn", + "no-unsafe-optional-chaining": "warn", + "no-unused-labels": "warn", + "no-unused-private-class-members": "warn", + "no-unused-vars": [ + "warn", + { + "args": "none" + } + ], + "no-useless-backreference": "warn", + "no-useless-catch": "warn", + "no-useless-escape": "warn", + "no-with": "warn", + "require-yield": "warn", + "use-isnan": "warn", + "valid-typeof": "warn", + "eqeqeq": [ + "warn", + "smart" + ], + "new-cap": "off", + "no-caller": "warn", + "no-console": "off", + "no-use-before-define": [ + "warn", + { + "functions": false + } + ] + } + }, + { + "files": [ + "src/**/*.js" + ], + "globals": { + "AbortController": "readonly", + "AbortSignal": "readonly", + "AbsoluteOrientationSensor": "readonly", + "AbstractRange": "readonly", + "Accelerometer": "readonly", + "addEventListener": "readonly", + "ai": "readonly", + "AI": "readonly", + "AICreateMonitor": "readonly", + "AITextSession": "readonly", + "alert": "readonly", + "AnalyserNode": "readonly", + "Animation": "readonly", + "AnimationEffect": "readonly", + "AnimationEvent": "readonly", + "AnimationPlaybackEvent": "readonly", + "AnimationTimeline": "readonly", + "AsyncDisposableStack": "readonly", + "atob": "readonly", + "Attr": "readonly", + "Audio": "readonly", + "AudioBuffer": "readonly", + "AudioBufferSourceNode": "readonly", + "AudioContext": "readonly", + "AudioData": "readonly", + "AudioDecoder": "readonly", + "AudioDestinationNode": "readonly", + "AudioEncoder": "readonly", + "AudioListener": "readonly", + "AudioNode": "readonly", + "AudioParam": "readonly", + "AudioParamMap": "readonly", + "AudioProcessingEvent": "readonly", + "AudioScheduledSourceNode": "readonly", + "AudioSinkInfo": "readonly", + "AudioWorklet": "readonly", + "AudioWorkletGlobalScope": "readonly", + "AudioWorkletNode": "readonly", + "AudioWorkletProcessor": "readonly", + "AuthenticatorAssertionResponse": "readonly", + "AuthenticatorAttestationResponse": "readonly", + "AuthenticatorResponse": "readonly", + "BackgroundFetchManager": "readonly", + "BackgroundFetchRecord": "readonly", + "BackgroundFetchRegistration": "readonly", + "BarcodeDetector": "readonly", + "BarProp": "readonly", + "BaseAudioContext": "readonly", + "BatteryManager": "readonly", + "BeforeUnloadEvent": "readonly", + "BiquadFilterNode": "readonly", + "Blob": "readonly", + "BlobEvent": "readonly", + "Bluetooth": "readonly", + "BluetoothCharacteristicProperties": "readonly", + "BluetoothDevice": "readonly", + "BluetoothRemoteGATTCharacteristic": "readonly", + "BluetoothRemoteGATTDescriptor": "readonly", + "BluetoothRemoteGATTServer": "readonly", + "BluetoothRemoteGATTService": "readonly", + "BluetoothUUID": "readonly", + "blur": "readonly", + "BroadcastChannel": "readonly", + "BrowserCaptureMediaStreamTrack": "readonly", + "btoa": "readonly", + "ByteLengthQueuingStrategy": "readonly", + "Cache": "readonly", + "caches": "readonly", + "CacheStorage": "readonly", + "cancelAnimationFrame": "readonly", + "cancelIdleCallback": "readonly", + "CanvasCaptureMediaStream": "readonly", + "CanvasCaptureMediaStreamTrack": "readonly", + "CanvasGradient": "readonly", + "CanvasPattern": "readonly", + "CanvasRenderingContext2D": "readonly", + "CaptureController": "readonly", + "CaretPosition": "readonly", + "CDATASection": "readonly", + "ChannelMergerNode": "readonly", + "ChannelSplitterNode": "readonly", + "ChapterInformation": "readonly", + "CharacterBoundsUpdateEvent": "readonly", + "CharacterData": "readonly", + "clearInterval": "readonly", + "clearTimeout": "readonly", + "clientInformation": "readonly", + "Clipboard": "readonly", + "ClipboardEvent": "readonly", + "ClipboardItem": "readonly", + "close": "readonly", + "closed": "readonly", + "CloseEvent": "readonly", + "CloseWatcher": "readonly", + "CommandEvent": "readonly", + "Comment": "readonly", + "CompositionEvent": "readonly", + "CompressionStream": "readonly", + "confirm": "readonly", + "console": "readonly", + "ConstantSourceNode": "readonly", + "ContentVisibilityAutoStateChangeEvent": "readonly", + "ConvolverNode": "readonly", + "CookieChangeEvent": "readonly", + "CookieDeprecationLabel": "readonly", + "cookieStore": "readonly", + "CookieStore": "readonly", + "CookieStoreManager": "readonly", + "CountQueuingStrategy": "readonly", + "createImageBitmap": "readonly", + "Credential": "readonly", + "credentialless": "readonly", + "CredentialsContainer": "readonly", + "CropTarget": "readonly", + "crossOriginIsolated": "readonly", + "crypto": "readonly", + "Crypto": "readonly", + "CryptoKey": "readonly", + "CSPViolationReportBody": "readonly", + "CSS": "readonly", + "CSSAnimation": "readonly", + "CSSConditionRule": "readonly", + "CSSContainerRule": "readonly", + "CSSCounterStyleRule": "readonly", + "CSSFontFaceRule": "readonly", + "CSSFontFeatureValuesRule": "readonly", + "CSSFontPaletteValuesRule": "readonly", + "CSSGroupingRule": "readonly", + "CSSImageValue": "readonly", + "CSSImportRule": "readonly", + "CSSKeyframeRule": "readonly", + "CSSKeyframesRule": "readonly", + "CSSKeywordValue": "readonly", + "CSSLayerBlockRule": "readonly", + "CSSLayerStatementRule": "readonly", + "CSSMarginRule": "readonly", + "CSSMathClamp": "readonly", + "CSSMathInvert": "readonly", + "CSSMathMax": "readonly", + "CSSMathMin": "readonly", + "CSSMathNegate": "readonly", + "CSSMathProduct": "readonly", + "CSSMathSum": "readonly", + "CSSMathValue": "readonly", + "CSSMatrixComponent": "readonly", + "CSSMediaRule": "readonly", + "CSSNamespaceRule": "readonly", + "CSSNestedDeclarations": "readonly", + "CSSNumericArray": "readonly", + "CSSNumericValue": "readonly", + "CSSPageDescriptors": "readonly", + "CSSPageRule": "readonly", + "CSSPerspective": "readonly", + "CSSPositionTryDescriptors": "readonly", + "CSSPositionTryRule": "readonly", + "CSSPositionValue": "readonly", + "CSSPropertyRule": "readonly", + "CSSRotate": "readonly", + "CSSRule": "readonly", + "CSSRuleList": "readonly", + "CSSScale": "readonly", + "CSSScopeRule": "readonly", + "CSSSkew": "readonly", + "CSSSkewX": "readonly", + "CSSSkewY": "readonly", + "CSSStartingStyleRule": "readonly", + "CSSStyleDeclaration": "readonly", + "CSSStyleRule": "readonly", + "CSSStyleSheet": "readonly", + "CSSStyleValue": "readonly", + "CSSSupportsRule": "readonly", + "CSSTransformComponent": "readonly", + "CSSTransformValue": "readonly", + "CSSTransition": "readonly", + "CSSTranslate": "readonly", + "CSSUnitValue": "readonly", + "CSSUnparsedValue": "readonly", + "CSSVariableReferenceValue": "readonly", + "CSSViewTransitionRule": "readonly", + "currentFrame": "readonly", + "currentTime": "readonly", + "CustomElementRegistry": "readonly", + "customElements": "readonly", + "CustomEvent": "readonly", + "CustomStateSet": "readonly", + "DataTransfer": "readonly", + "DataTransferItem": "readonly", + "DataTransferItemList": "readonly", + "DecompressionStream": "readonly", + "DelayNode": "readonly", + "DelegatedInkTrailPresenter": "readonly", + "DeviceMotionEvent": "readonly", + "DeviceMotionEventAcceleration": "readonly", + "DeviceMotionEventRotationRate": "readonly", + "DeviceOrientationEvent": "readonly", + "devicePixelRatio": "readonly", + "DevicePosture": "readonly", + "dispatchEvent": "readonly", + "DisposableStack": "readonly", + "document": "readonly", + "Document": "readonly", + "DocumentFragment": "readonly", + "documentPictureInPicture": "readonly", + "DocumentPictureInPicture": "readonly", + "DocumentPictureInPictureEvent": "readonly", + "DocumentTimeline": "readonly", + "DocumentType": "readonly", + "DOMError": "readonly", + "DOMException": "readonly", + "DOMImplementation": "readonly", + "DOMMatrix": "readonly", + "DOMMatrixReadOnly": "readonly", + "DOMParser": "readonly", + "DOMPoint": "readonly", + "DOMPointReadOnly": "readonly", + "DOMQuad": "readonly", + "DOMRect": "readonly", + "DOMRectList": "readonly", + "DOMRectReadOnly": "readonly", + "DOMStringList": "readonly", + "DOMStringMap": "readonly", + "DOMTokenList": "readonly", + "DragEvent": "readonly", + "DynamicsCompressorNode": "readonly", + "EditContext": "readonly", + "Element": "readonly", + "ElementInternals": "readonly", + "EncodedAudioChunk": "readonly", + "EncodedVideoChunk": "readonly", + "ErrorEvent": "readonly", + "event": "readonly", + "Event": "readonly", + "EventCounts": "readonly", + "EventSource": "readonly", + "EventTarget": "readonly", + "external": "readonly", + "External": "readonly", + "EyeDropper": "readonly", + "FeaturePolicy": "readonly", + "FederatedCredential": "readonly", + "fence": "readonly", + "Fence": "readonly", + "FencedFrameConfig": "readonly", + "fetch": "readonly", + "fetchLater": "readonly", + "FetchLaterResult": "readonly", + "File": "readonly", + "FileList": "readonly", + "FileReader": "readonly", + "FileSystem": "readonly", + "FileSystemDirectoryEntry": "readonly", + "FileSystemDirectoryHandle": "readonly", + "FileSystemDirectoryReader": "readonly", + "FileSystemEntry": "readonly", + "FileSystemFileEntry": "readonly", + "FileSystemFileHandle": "readonly", + "FileSystemHandle": "readonly", + "FileSystemObserver": "readonly", + "FileSystemWritableFileStream": "readonly", + "find": "readonly", + "focus": "readonly", + "FocusEvent": "readonly", + "FontData": "readonly", + "FontFace": "readonly", + "FontFaceSet": "readonly", + "FontFaceSetLoadEvent": "readonly", + "FormData": "readonly", + "FormDataEvent": "readonly", + "FragmentDirective": "readonly", + "frameElement": "readonly", + "frames": "readonly", + "GainNode": "readonly", + "Gamepad": "readonly", + "GamepadAxisMoveEvent": "readonly", + "GamepadButton": "readonly", + "GamepadButtonEvent": "readonly", + "GamepadEvent": "readonly", + "GamepadHapticActuator": "readonly", + "GamepadPose": "readonly", + "Geolocation": "readonly", + "GeolocationCoordinates": "readonly", + "GeolocationPosition": "readonly", + "GeolocationPositionError": "readonly", + "getComputedStyle": "readonly", + "getScreenDetails": "readonly", + "getSelection": "readonly", + "GPU": "readonly", + "GPUAdapter": "readonly", + "GPUAdapterInfo": "readonly", + "GPUBindGroup": "readonly", + "GPUBindGroupLayout": "readonly", + "GPUBuffer": "readonly", + "GPUBufferUsage": "readonly", + "GPUCanvasContext": "readonly", + "GPUColorWrite": "readonly", + "GPUCommandBuffer": "readonly", + "GPUCommandEncoder": "readonly", + "GPUCompilationInfo": "readonly", + "GPUCompilationMessage": "readonly", + "GPUComputePassEncoder": "readonly", + "GPUComputePipeline": "readonly", + "GPUDevice": "readonly", + "GPUDeviceLostInfo": "readonly", + "GPUError": "readonly", + "GPUExternalTexture": "readonly", + "GPUInternalError": "readonly", + "GPUMapMode": "readonly", + "GPUOutOfMemoryError": "readonly", + "GPUPipelineError": "readonly", + "GPUPipelineLayout": "readonly", + "GPUQuerySet": "readonly", + "GPUQueue": "readonly", + "GPURenderBundle": "readonly", + "GPURenderBundleEncoder": "readonly", + "GPURenderPassEncoder": "readonly", + "GPURenderPipeline": "readonly", + "GPUSampler": "readonly", + "GPUShaderModule": "readonly", + "GPUShaderStage": "readonly", + "GPUSupportedFeatures": "readonly", + "GPUSupportedLimits": "readonly", + "GPUTexture": "readonly", + "GPUTextureUsage": "readonly", + "GPUTextureView": "readonly", + "GPUUncapturedErrorEvent": "readonly", + "GPUValidationError": "readonly", + "GravitySensor": "readonly", + "Gyroscope": "readonly", + "HashChangeEvent": "readonly", + "Headers": "readonly", + "HID": "readonly", + "HIDConnectionEvent": "readonly", + "HIDDevice": "readonly", + "HIDInputReportEvent": "readonly", + "Highlight": "readonly", + "HighlightRegistry": "readonly", + "history": "readonly", + "History": "readonly", + "HTMLAllCollection": "readonly", + "HTMLAnchorElement": "readonly", + "HTMLAreaElement": "readonly", + "HTMLAudioElement": "readonly", + "HTMLBaseElement": "readonly", + "HTMLBodyElement": "readonly", + "HTMLBRElement": "readonly", + "HTMLButtonElement": "readonly", + "HTMLCanvasElement": "readonly", + "HTMLCollection": "readonly", + "HTMLDataElement": "readonly", + "HTMLDataListElement": "readonly", + "HTMLDetailsElement": "readonly", + "HTMLDialogElement": "readonly", + "HTMLDirectoryElement": "readonly", + "HTMLDivElement": "readonly", + "HTMLDListElement": "readonly", + "HTMLDocument": "readonly", + "HTMLElement": "readonly", + "HTMLEmbedElement": "readonly", + "HTMLFencedFrameElement": "readonly", + "HTMLFieldSetElement": "readonly", + "HTMLFontElement": "readonly", + "HTMLFormControlsCollection": "readonly", + "HTMLFormElement": "readonly", + "HTMLFrameElement": "readonly", + "HTMLFrameSetElement": "readonly", + "HTMLHeadElement": "readonly", + "HTMLHeadingElement": "readonly", + "HTMLHRElement": "readonly", + "HTMLHtmlElement": "readonly", + "HTMLIFrameElement": "readonly", + "HTMLImageElement": "readonly", + "HTMLInputElement": "readonly", + "HTMLLabelElement": "readonly", + "HTMLLegendElement": "readonly", + "HTMLLIElement": "readonly", + "HTMLLinkElement": "readonly", + "HTMLMapElement": "readonly", + "HTMLMarqueeElement": "readonly", + "HTMLMediaElement": "readonly", + "HTMLMenuElement": "readonly", + "HTMLMetaElement": "readonly", + "HTMLMeterElement": "readonly", + "HTMLModElement": "readonly", + "HTMLObjectElement": "readonly", + "HTMLOListElement": "readonly", + "HTMLOptGroupElement": "readonly", + "HTMLOptionElement": "readonly", + "HTMLOptionsCollection": "readonly", + "HTMLOutputElement": "readonly", + "HTMLParagraphElement": "readonly", + "HTMLParamElement": "readonly", + "HTMLPictureElement": "readonly", + "HTMLPreElement": "readonly", + "HTMLProgressElement": "readonly", + "HTMLQuoteElement": "readonly", + "HTMLScriptElement": "readonly", + "HTMLSelectedContentElement": "readonly", + "HTMLSelectElement": "readonly", + "HTMLSlotElement": "readonly", + "HTMLSourceElement": "readonly", + "HTMLSpanElement": "readonly", + "HTMLStyleElement": "readonly", + "HTMLTableCaptionElement": "readonly", + "HTMLTableCellElement": "readonly", + "HTMLTableColElement": "readonly", + "HTMLTableElement": "readonly", + "HTMLTableRowElement": "readonly", + "HTMLTableSectionElement": "readonly", + "HTMLTemplateElement": "readonly", + "HTMLTextAreaElement": "readonly", + "HTMLTimeElement": "readonly", + "HTMLTitleElement": "readonly", + "HTMLTrackElement": "readonly", + "HTMLUListElement": "readonly", + "HTMLUnknownElement": "readonly", + "HTMLVideoElement": "readonly", + "IDBCursor": "readonly", + "IDBCursorWithValue": "readonly", + "IDBDatabase": "readonly", + "IDBFactory": "readonly", + "IDBIndex": "readonly", + "IDBKeyRange": "readonly", + "IDBObjectStore": "readonly", + "IDBOpenDBRequest": "readonly", + "IDBRequest": "readonly", + "IDBTransaction": "readonly", + "IDBVersionChangeEvent": "readonly", + "IdentityCredential": "readonly", + "IdentityCredentialError": "readonly", + "IdentityProvider": "readonly", + "IdleDeadline": "readonly", + "IdleDetector": "readonly", + "IIRFilterNode": "readonly", + "Image": "readonly", + "ImageBitmap": "readonly", + "ImageBitmapRenderingContext": "readonly", + "ImageCapture": "readonly", + "ImageData": "readonly", + "ImageDecoder": "readonly", + "ImageTrack": "readonly", + "ImageTrackList": "readonly", + "indexedDB": "readonly", + "Ink": "readonly", + "innerHeight": "readonly", + "innerWidth": "readonly", + "InputDeviceCapabilities": "readonly", + "InputDeviceInfo": "readonly", + "InputEvent": "readonly", + "IntersectionObserver": "readonly", + "IntersectionObserverEntry": "readonly", + "isSecureContext": "readonly", + "Keyboard": "readonly", + "KeyboardEvent": "readonly", + "KeyboardLayoutMap": "readonly", + "KeyframeEffect": "readonly", + "LanguageDetector": "readonly", + "LargestContentfulPaint": "readonly", + "LaunchParams": "readonly", + "launchQueue": "readonly", + "LaunchQueue": "readonly", + "LayoutShift": "readonly", + "LayoutShiftAttribution": "readonly", + "length": "readonly", + "LinearAccelerationSensor": "readonly", + "localStorage": "readonly", + "location": "writable", + "Location": "readonly", + "locationbar": "readonly", + "Lock": "readonly", + "LockManager": "readonly", + "matchMedia": "readonly", + "MathMLElement": "readonly", + "MediaCapabilities": "readonly", + "MediaCapabilitiesInfo": "readonly", + "MediaDeviceInfo": "readonly", + "MediaDevices": "readonly", + "MediaElementAudioSourceNode": "readonly", + "MediaEncryptedEvent": "readonly", + "MediaError": "readonly", + "MediaKeyError": "readonly", + "MediaKeyMessageEvent": "readonly", + "MediaKeys": "readonly", + "MediaKeySession": "readonly", + "MediaKeyStatusMap": "readonly", + "MediaKeySystemAccess": "readonly", + "MediaList": "readonly", + "MediaMetadata": "readonly", + "MediaQueryList": "readonly", + "MediaQueryListEvent": "readonly", + "MediaRecorder": "readonly", + "MediaRecorderErrorEvent": "readonly", + "MediaSession": "readonly", + "MediaSource": "readonly", + "MediaSourceHandle": "readonly", + "MediaStream": "readonly", + "MediaStreamAudioDestinationNode": "readonly", + "MediaStreamAudioSourceNode": "readonly", + "MediaStreamEvent": "readonly", + "MediaStreamTrack": "readonly", + "MediaStreamTrackAudioSourceNode": "readonly", + "MediaStreamTrackAudioStats": "readonly", + "MediaStreamTrackEvent": "readonly", + "MediaStreamTrackGenerator": "readonly", + "MediaStreamTrackProcessor": "readonly", + "MediaStreamTrackVideoStats": "readonly", + "menubar": "readonly", + "MessageChannel": "readonly", + "MessageEvent": "readonly", + "MessagePort": "readonly", + "MIDIAccess": "readonly", + "MIDIConnectionEvent": "readonly", + "MIDIInput": "readonly", + "MIDIInputMap": "readonly", + "MIDIMessageEvent": "readonly", + "MIDIOutput": "readonly", + "MIDIOutputMap": "readonly", + "MIDIPort": "readonly", + "MimeType": "readonly", + "MimeTypeArray": "readonly", + "model": "readonly", + "ModelGenericSession": "readonly", + "ModelManager": "readonly", + "MouseEvent": "readonly", + "moveBy": "readonly", + "moveTo": "readonly", + "MutationEvent": "readonly", + "MutationObserver": "readonly", + "MutationRecord": "readonly", + "name": "readonly", + "NamedNodeMap": "readonly", + "NavigateEvent": "readonly", + "navigation": "readonly", + "Navigation": "readonly", + "NavigationActivation": "readonly", + "NavigationCurrentEntryChangeEvent": "readonly", + "NavigationDestination": "readonly", + "NavigationHistoryEntry": "readonly", + "NavigationPreloadManager": "readonly", + "NavigationTransition": "readonly", + "navigator": "readonly", + "Navigator": "readonly", + "NavigatorLogin": "readonly", + "NavigatorManagedData": "readonly", + "NavigatorUAData": "readonly", + "NetworkInformation": "readonly", + "Node": "readonly", + "NodeFilter": "readonly", + "NodeIterator": "readonly", + "NodeList": "readonly", + "Notification": "readonly", + "NotifyPaintEvent": "readonly", + "NotRestoredReasonDetails": "readonly", + "NotRestoredReasons": "readonly", + "Observable": "readonly", + "OfflineAudioCompletionEvent": "readonly", + "OfflineAudioContext": "readonly", + "offscreenBuffering": "readonly", + "OffscreenCanvas": "readonly", + "OffscreenCanvasRenderingContext2D": "readonly", + "onabort": "writable", + "onafterprint": "writable", + "onanimationcancel": "writable", + "onanimationend": "writable", + "onanimationiteration": "writable", + "onanimationstart": "writable", + "onappinstalled": "writable", + "onauxclick": "writable", + "onbeforeinput": "writable", + "onbeforeinstallprompt": "writable", + "onbeforematch": "writable", + "onbeforeprint": "writable", + "onbeforetoggle": "writable", + "onbeforeunload": "writable", + "onbeforexrselect": "writable", + "onblur": "writable", + "oncancel": "writable", + "oncanplay": "writable", + "oncanplaythrough": "writable", + "onchange": "writable", + "onclick": "writable", + "onclose": "writable", + "oncommand": "writable", + "oncontentvisibilityautostatechange": "writable", + "oncontextlost": "writable", + "oncontextmenu": "writable", + "oncontextrestored": "writable", + "oncopy": "writable", + "oncuechange": "writable", + "oncut": "writable", + "ondblclick": "writable", + "ondevicemotion": "writable", + "ondeviceorientation": "writable", + "ondeviceorientationabsolute": "writable", + "ondrag": "writable", + "ondragend": "writable", + "ondragenter": "writable", + "ondragleave": "writable", + "ondragover": "writable", + "ondragstart": "writable", + "ondrop": "writable", + "ondurationchange": "writable", + "onemptied": "writable", + "onended": "writable", + "onerror": "writable", + "onfocus": "writable", + "onformdata": "writable", + "ongamepadconnected": "writable", + "ongamepaddisconnected": "writable", + "ongotpointercapture": "writable", + "onhashchange": "writable", + "oninput": "writable", + "oninvalid": "writable", + "onkeydown": "writable", + "onkeypress": "writable", + "onkeyup": "writable", + "onlanguagechange": "writable", + "onload": "writable", + "onloadeddata": "writable", + "onloadedmetadata": "writable", + "onloadstart": "writable", + "onlostpointercapture": "writable", + "onmessage": "writable", + "onmessageerror": "writable", + "onmousedown": "writable", + "onmouseenter": "writable", + "onmouseleave": "writable", + "onmousemove": "writable", + "onmouseout": "writable", + "onmouseover": "writable", + "onmouseup": "writable", + "onmousewheel": "writable", + "onoffline": "writable", + "ononline": "writable", + "onpagehide": "writable", + "onpagereveal": "writable", + "onpageshow": "writable", + "onpageswap": "writable", + "onpaste": "writable", + "onpause": "writable", + "onplay": "writable", + "onplaying": "writable", + "onpointercancel": "writable", + "onpointerdown": "writable", + "onpointerenter": "writable", + "onpointerleave": "writable", + "onpointermove": "writable", + "onpointerout": "writable", + "onpointerover": "writable", + "onpointerrawupdate": "writable", + "onpointerup": "writable", + "onpopstate": "writable", + "onprogress": "writable", + "onratechange": "writable", + "onrejectionhandled": "writable", + "onreset": "writable", + "onresize": "writable", + "onscroll": "writable", + "onscrollend": "writable", + "onscrollsnapchange": "writable", + "onscrollsnapchanging": "writable", + "onsearch": "writable", + "onsecuritypolicyviolation": "writable", + "onseeked": "writable", + "onseeking": "writable", + "onselect": "writable", + "onselectionchange": "writable", + "onselectstart": "writable", + "onslotchange": "writable", + "onstalled": "writable", + "onstorage": "writable", + "onsubmit": "writable", + "onsuspend": "writable", + "ontimeupdate": "writable", + "ontoggle": "writable", + "ontransitioncancel": "writable", + "ontransitionend": "writable", + "ontransitionrun": "writable", + "ontransitionstart": "writable", + "onunhandledrejection": "writable", + "onunload": "writable", + "onvolumechange": "writable", + "onwaiting": "writable", + "onwheel": "writable", + "open": "readonly", + "opener": "readonly", + "Option": "readonly", + "OrientationSensor": "readonly", + "origin": "readonly", + "originAgentCluster": "readonly", + "OscillatorNode": "readonly", + "OTPCredential": "readonly", + "outerHeight": "readonly", + "outerWidth": "readonly", + "OverconstrainedError": "readonly", + "PageRevealEvent": "readonly", + "PageSwapEvent": "readonly", + "PageTransitionEvent": "readonly", + "pageXOffset": "readonly", + "pageYOffset": "readonly", + "PannerNode": "readonly", + "parent": "readonly", + "PasswordCredential": "readonly", + "Path2D": "readonly", + "PaymentAddress": "readonly", + "PaymentManager": "readonly", + "PaymentMethodChangeEvent": "readonly", + "PaymentRequest": "readonly", + "PaymentRequestUpdateEvent": "readonly", + "PaymentResponse": "readonly", + "performance": "readonly", + "Performance": "readonly", + "PerformanceElementTiming": "readonly", + "PerformanceEntry": "readonly", + "PerformanceEventTiming": "readonly", + "PerformanceLongAnimationFrameTiming": "readonly", + "PerformanceLongTaskTiming": "readonly", + "PerformanceMark": "readonly", + "PerformanceMeasure": "readonly", + "PerformanceNavigation": "readonly", + "PerformanceNavigationTiming": "readonly", + "PerformanceObserver": "readonly", + "PerformanceObserverEntryList": "readonly", + "PerformancePaintTiming": "readonly", + "PerformanceResourceTiming": "readonly", + "PerformanceScriptTiming": "readonly", + "PerformanceServerTiming": "readonly", + "PerformanceTiming": "readonly", + "PeriodicSyncManager": "readonly", + "PeriodicWave": "readonly", + "Permissions": "readonly", + "PermissionStatus": "readonly", + "PERSISTENT": "readonly", + "personalbar": "readonly", + "PictureInPictureEvent": "readonly", + "PictureInPictureWindow": "readonly", + "Plugin": "readonly", + "PluginArray": "readonly", + "PointerEvent": "readonly", + "PopStateEvent": "readonly", + "postMessage": "readonly", + "Presentation": "readonly", + "PresentationAvailability": "readonly", + "PresentationConnection": "readonly", + "PresentationConnectionAvailableEvent": "readonly", + "PresentationConnectionCloseEvent": "readonly", + "PresentationConnectionList": "readonly", + "PresentationReceiver": "readonly", + "PresentationRequest": "readonly", + "PressureObserver": "readonly", + "PressureRecord": "readonly", + "print": "readonly", + "ProcessingInstruction": "readonly", + "Profiler": "readonly", + "ProgressEvent": "readonly", + "PromiseRejectionEvent": "readonly", + "prompt": "readonly", + "ProtectedAudience": "readonly", + "PublicKeyCredential": "readonly", + "PushManager": "readonly", + "PushSubscription": "readonly", + "PushSubscriptionOptions": "readonly", + "queryLocalFonts": "readonly", + "queueMicrotask": "readonly", + "RadioNodeList": "readonly", + "Range": "readonly", + "ReadableByteStreamController": "readonly", + "ReadableStream": "readonly", + "ReadableStreamBYOBReader": "readonly", + "ReadableStreamBYOBRequest": "readonly", + "ReadableStreamDefaultController": "readonly", + "ReadableStreamDefaultReader": "readonly", + "registerProcessor": "readonly", + "RelativeOrientationSensor": "readonly", + "RemotePlayback": "readonly", + "removeEventListener": "readonly", + "ReportBody": "readonly", + "reportError": "readonly", + "ReportingObserver": "readonly", + "Request": "readonly", + "requestAnimationFrame": "readonly", + "requestIdleCallback": "readonly", + "resizeBy": "readonly", + "ResizeObserver": "readonly", + "ResizeObserverEntry": "readonly", + "ResizeObserverSize": "readonly", + "resizeTo": "readonly", + "Response": "readonly", + "RestrictionTarget": "readonly", + "RTCCertificate": "readonly", + "RTCDataChannel": "readonly", + "RTCDataChannelEvent": "readonly", + "RTCDtlsTransport": "readonly", + "RTCDTMFSender": "readonly", + "RTCDTMFToneChangeEvent": "readonly", + "RTCEncodedAudioFrame": "readonly", + "RTCEncodedVideoFrame": "readonly", + "RTCError": "readonly", + "RTCErrorEvent": "readonly", + "RTCIceCandidate": "readonly", + "RTCIceTransport": "readonly", + "RTCPeerConnection": "readonly", + "RTCPeerConnectionIceErrorEvent": "readonly", + "RTCPeerConnectionIceEvent": "readonly", + "RTCRtpReceiver": "readonly", + "RTCRtpScriptTransform": "readonly", + "RTCRtpSender": "readonly", + "RTCRtpTransceiver": "readonly", + "RTCSctpTransport": "readonly", + "RTCSessionDescription": "readonly", + "RTCStatsReport": "readonly", + "RTCTrackEvent": "readonly", + "sampleRate": "readonly", + "scheduler": "readonly", + "Scheduler": "readonly", + "Scheduling": "readonly", + "screen": "readonly", + "Screen": "readonly", + "ScreenDetailed": "readonly", + "ScreenDetails": "readonly", + "screenLeft": "readonly", + "ScreenOrientation": "readonly", + "screenTop": "readonly", + "screenX": "readonly", + "screenY": "readonly", + "ScriptProcessorNode": "readonly", + "scroll": "readonly", + "scrollbars": "readonly", + "scrollBy": "readonly", + "ScrollTimeline": "readonly", + "scrollTo": "readonly", + "scrollX": "readonly", + "scrollY": "readonly", + "SecurityPolicyViolationEvent": "readonly", + "Selection": "readonly", + "self": "readonly", + "Sensor": "readonly", + "SensorErrorEvent": "readonly", + "Serial": "readonly", + "SerialPort": "readonly", + "ServiceWorker": "readonly", + "ServiceWorkerContainer": "readonly", + "ServiceWorkerRegistration": "readonly", + "sessionStorage": "readonly", + "setInterval": "readonly", + "setTimeout": "readonly", + "ShadowRoot": "readonly", + "sharedStorage": "readonly", + "SharedStorage": "readonly", + "SharedStorageAppendMethod": "readonly", + "SharedStorageClearMethod": "readonly", + "SharedStorageDeleteMethod": "readonly", + "SharedStorageModifierMethod": "readonly", + "SharedStorageSetMethod": "readonly", + "SharedStorageWorklet": "readonly", + "SharedWorker": "readonly", + "showDirectoryPicker": "readonly", + "showOpenFilePicker": "readonly", + "showSaveFilePicker": "readonly", + "SnapEvent": "readonly", + "SourceBuffer": "readonly", + "SourceBufferList": "readonly", + "speechSynthesis": "readonly", + "SpeechSynthesis": "readonly", + "SpeechSynthesisErrorEvent": "readonly", + "SpeechSynthesisEvent": "readonly", + "SpeechSynthesisUtterance": "readonly", + "SpeechSynthesisVoice": "readonly", + "StaticRange": "readonly", + "status": "readonly", + "statusbar": "readonly", + "StereoPannerNode": "readonly", + "stop": "readonly", + "Storage": "readonly", + "StorageBucket": "readonly", + "StorageBucketManager": "readonly", + "StorageEvent": "readonly", + "StorageManager": "readonly", + "structuredClone": "readonly", + "styleMedia": "readonly", + "StylePropertyMap": "readonly", + "StylePropertyMapReadOnly": "readonly", + "StyleSheet": "readonly", + "StyleSheetList": "readonly", + "SubmitEvent": "readonly", + "Subscriber": "readonly", + "SubtleCrypto": "readonly", + "SuppressedError": "readonly", + "SVGAElement": "readonly", + "SVGAngle": "readonly", + "SVGAnimatedAngle": "readonly", + "SVGAnimatedBoolean": "readonly", + "SVGAnimatedEnumeration": "readonly", + "SVGAnimatedInteger": "readonly", + "SVGAnimatedLength": "readonly", + "SVGAnimatedLengthList": "readonly", + "SVGAnimatedNumber": "readonly", + "SVGAnimatedNumberList": "readonly", + "SVGAnimatedPreserveAspectRatio": "readonly", + "SVGAnimatedRect": "readonly", + "SVGAnimatedString": "readonly", + "SVGAnimatedTransformList": "readonly", + "SVGAnimateElement": "readonly", + "SVGAnimateMotionElement": "readonly", + "SVGAnimateTransformElement": "readonly", + "SVGAnimationElement": "readonly", + "SVGCircleElement": "readonly", + "SVGClipPathElement": "readonly", + "SVGComponentTransferFunctionElement": "readonly", + "SVGDefsElement": "readonly", + "SVGDescElement": "readonly", + "SVGElement": "readonly", + "SVGEllipseElement": "readonly", + "SVGFEBlendElement": "readonly", + "SVGFEColorMatrixElement": "readonly", + "SVGFEComponentTransferElement": "readonly", + "SVGFECompositeElement": "readonly", + "SVGFEConvolveMatrixElement": "readonly", + "SVGFEDiffuseLightingElement": "readonly", + "SVGFEDisplacementMapElement": "readonly", + "SVGFEDistantLightElement": "readonly", + "SVGFEDropShadowElement": "readonly", + "SVGFEFloodElement": "readonly", + "SVGFEFuncAElement": "readonly", + "SVGFEFuncBElement": "readonly", + "SVGFEFuncGElement": "readonly", + "SVGFEFuncRElement": "readonly", + "SVGFEGaussianBlurElement": "readonly", + "SVGFEImageElement": "readonly", + "SVGFEMergeElement": "readonly", + "SVGFEMergeNodeElement": "readonly", + "SVGFEMorphologyElement": "readonly", + "SVGFEOffsetElement": "readonly", + "SVGFEPointLightElement": "readonly", + "SVGFESpecularLightingElement": "readonly", + "SVGFESpotLightElement": "readonly", + "SVGFETileElement": "readonly", + "SVGFETurbulenceElement": "readonly", + "SVGFilterElement": "readonly", + "SVGForeignObjectElement": "readonly", + "SVGGElement": "readonly", + "SVGGeometryElement": "readonly", + "SVGGradientElement": "readonly", + "SVGGraphicsElement": "readonly", + "SVGImageElement": "readonly", + "SVGLength": "readonly", + "SVGLengthList": "readonly", + "SVGLinearGradientElement": "readonly", + "SVGLineElement": "readonly", + "SVGMarkerElement": "readonly", + "SVGMaskElement": "readonly", + "SVGMatrix": "readonly", + "SVGMetadataElement": "readonly", + "SVGMPathElement": "readonly", + "SVGNumber": "readonly", + "SVGNumberList": "readonly", + "SVGPathElement": "readonly", + "SVGPatternElement": "readonly", + "SVGPoint": "readonly", + "SVGPointList": "readonly", + "SVGPolygonElement": "readonly", + "SVGPolylineElement": "readonly", + "SVGPreserveAspectRatio": "readonly", + "SVGRadialGradientElement": "readonly", + "SVGRect": "readonly", + "SVGRectElement": "readonly", + "SVGScriptElement": "readonly", + "SVGSetElement": "readonly", + "SVGStopElement": "readonly", + "SVGStringList": "readonly", + "SVGStyleElement": "readonly", + "SVGSVGElement": "readonly", + "SVGSwitchElement": "readonly", + "SVGSymbolElement": "readonly", + "SVGTextContentElement": "readonly", + "SVGTextElement": "readonly", + "SVGTextPathElement": "readonly", + "SVGTextPositioningElement": "readonly", + "SVGTitleElement": "readonly", + "SVGTransform": "readonly", + "SVGTransformList": "readonly", + "SVGTSpanElement": "readonly", + "SVGUnitTypes": "readonly", + "SVGUseElement": "readonly", + "SVGViewElement": "readonly", + "SyncManager": "readonly", + "TaskAttributionTiming": "readonly", + "TaskController": "readonly", + "TaskPriorityChangeEvent": "readonly", + "TaskSignal": "readonly", + "TEMPORARY": "readonly", + "Text": "readonly", + "TextDecoder": "readonly", + "TextDecoderStream": "readonly", + "TextEncoder": "readonly", + "TextEncoderStream": "readonly", + "TextEvent": "readonly", + "TextFormat": "readonly", + "TextFormatUpdateEvent": "readonly", + "TextMetrics": "readonly", + "TextTrack": "readonly", + "TextTrackCue": "readonly", + "TextTrackCueList": "readonly", + "TextTrackList": "readonly", + "TextUpdateEvent": "readonly", + "TimeEvent": "readonly", + "TimeRanges": "readonly", + "ToggleEvent": "readonly", + "toolbar": "readonly", + "top": "readonly", + "Touch": "readonly", + "TouchEvent": "readonly", + "TouchList": "readonly", + "TrackEvent": "readonly", + "TransformStream": "readonly", + "TransformStreamDefaultController": "readonly", + "TransitionEvent": "readonly", + "TreeWalker": "readonly", + "TrustedHTML": "readonly", + "TrustedScript": "readonly", + "TrustedScriptURL": "readonly", + "TrustedTypePolicy": "readonly", + "TrustedTypePolicyFactory": "readonly", + "trustedTypes": "readonly", + "UIEvent": "readonly", + "URL": "readonly", + "URLPattern": "readonly", + "URLSearchParams": "readonly", + "USB": "readonly", + "USBAlternateInterface": "readonly", + "USBConfiguration": "readonly", + "USBConnectionEvent": "readonly", + "USBDevice": "readonly", + "USBEndpoint": "readonly", + "USBInterface": "readonly", + "USBInTransferResult": "readonly", + "USBIsochronousInTransferPacket": "readonly", + "USBIsochronousInTransferResult": "readonly", + "USBIsochronousOutTransferPacket": "readonly", + "USBIsochronousOutTransferResult": "readonly", + "USBOutTransferResult": "readonly", + "UserActivation": "readonly", + "ValidityState": "readonly", + "VideoColorSpace": "readonly", + "VideoDecoder": "readonly", + "VideoEncoder": "readonly", + "VideoFrame": "readonly", + "VideoPlaybackQuality": "readonly", + "ViewTimeline": "readonly", + "ViewTransition": "readonly", + "ViewTransitionTypeSet": "readonly", + "VirtualKeyboard": "readonly", + "VirtualKeyboardGeometryChangeEvent": "readonly", + "VisibilityStateEntry": "readonly", + "visualViewport": "readonly", + "VisualViewport": "readonly", + "VTTCue": "readonly", + "VTTRegion": "readonly", + "WakeLock": "readonly", + "WakeLockSentinel": "readonly", + "WaveShaperNode": "readonly", + "WebAssembly": "readonly", + "WebGL2RenderingContext": "readonly", + "WebGLActiveInfo": "readonly", + "WebGLBuffer": "readonly", + "WebGLContextEvent": "readonly", + "WebGLFramebuffer": "readonly", + "WebGLObject": "readonly", + "WebGLProgram": "readonly", + "WebGLQuery": "readonly", + "WebGLRenderbuffer": "readonly", + "WebGLRenderingContext": "readonly", + "WebGLSampler": "readonly", + "WebGLShader": "readonly", + "WebGLShaderPrecisionFormat": "readonly", + "WebGLSync": "readonly", + "WebGLTexture": "readonly", + "WebGLTransformFeedback": "readonly", + "WebGLUniformLocation": "readonly", + "WebGLVertexArrayObject": "readonly", + "WebSocket": "readonly", + "WebSocketError": "readonly", + "WebSocketStream": "readonly", + "WebTransport": "readonly", + "WebTransportBidirectionalStream": "readonly", + "WebTransportDatagramDuplexStream": "readonly", + "WebTransportError": "readonly", + "WebTransportReceiveStream": "readonly", + "WebTransportSendStream": "readonly", + "WGSLLanguageFeatures": "readonly", + "WheelEvent": "readonly", + "when": "readonly", + "window": "readonly", + "Window": "readonly", + "WindowControlsOverlay": "readonly", + "WindowControlsOverlayGeometryChangeEvent": "readonly", + "Worker": "readonly", + "Worklet": "readonly", + "WorkletGlobalScope": "readonly", + "WritableStream": "readonly", + "WritableStreamDefaultController": "readonly", + "WritableStreamDefaultWriter": "readonly", + "XMLDocument": "readonly", + "XMLHttpRequest": "readonly", + "XMLHttpRequestEventTarget": "readonly", + "XMLHttpRequestUpload": "readonly", + "XMLSerializer": "readonly", + "XPathEvaluator": "readonly", + "XPathExpression": "readonly", + "XPathResult": "readonly", + "XRAnchor": "readonly", + "XRAnchorSet": "readonly", + "XRBoundedReferenceSpace": "readonly", + "XRCamera": "readonly", + "XRCPUDepthInformation": "readonly", + "XRDepthInformation": "readonly", + "XRDOMOverlayState": "readonly", + "XRFrame": "readonly", + "XRHand": "readonly", + "XRHitTestResult": "readonly", + "XRHitTestSource": "readonly", + "XRInputSource": "readonly", + "XRInputSourceArray": "readonly", + "XRInputSourceEvent": "readonly", + "XRInputSourcesChangeEvent": "readonly", + "XRJointPose": "readonly", + "XRJointSpace": "readonly", + "XRLayer": "readonly", + "XRLightEstimate": "readonly", + "XRLightProbe": "readonly", + "XRPose": "readonly", + "XRRay": "readonly", + "XRReferenceSpace": "readonly", + "XRReferenceSpaceEvent": "readonly", + "XRRenderState": "readonly", + "XRRigidTransform": "readonly", + "XRSession": "readonly", + "XRSessionEvent": "readonly", + "XRSpace": "readonly", + "XRSystem": "readonly", + "XRTransientInputHitTestResult": "readonly", + "XRTransientInputHitTestSource": "readonly", + "XRView": "readonly", + "XRViewerPose": "readonly", + "XRViewport": "readonly", + "XRWebGLBinding": "readonly", + "XRWebGLDepthInformation": "readonly", + "XRWebGLLayer": "readonly", + "XSLTProcessor": "readonly" + }, + "env": { + "es2024": true + } + }, + { + "files": [ + "test/**/*.js", + "utils/**/*.js", + "**/*.config.mjs", + "vitest.workspace.mjs" + ], + "globals": { + "AbortController": "readonly", + "AbortSignal": "readonly", + "AsyncDisposableStack": "readonly", + "atob": "readonly", + "Blob": "readonly", + "BroadcastChannel": "readonly", + "btoa": "readonly", + "Buffer": "readonly", + "ByteLengthQueuingStrategy": "readonly", + "clearImmediate": "readonly", + "clearInterval": "readonly", + "clearTimeout": "readonly", + "CloseEvent": "readonly", + "CompressionStream": "readonly", + "console": "readonly", + "CountQueuingStrategy": "readonly", + "crypto": "readonly", + "Crypto": "readonly", + "CryptoKey": "readonly", + "CustomEvent": "readonly", + "DecompressionStream": "readonly", + "DisposableStack": "readonly", + "DOMException": "readonly", + "Event": "readonly", + "EventTarget": "readonly", + "fetch": "readonly", + "File": "readonly", + "FormData": "readonly", + "global": "readonly", + "Headers": "readonly", + "MessageChannel": "readonly", + "MessageEvent": "readonly", + "MessagePort": "readonly", + "navigator": "readonly", + "Navigator": "readonly", + "performance": "readonly", + "Performance": "readonly", + "PerformanceEntry": "readonly", + "PerformanceMark": "readonly", + "PerformanceMeasure": "readonly", + "PerformanceObserver": "readonly", + "PerformanceObserverEntryList": "readonly", + "PerformanceResourceTiming": "readonly", + "process": "readonly", + "queueMicrotask": "readonly", + "ReadableByteStreamController": "readonly", + "ReadableStream": "readonly", + "ReadableStreamBYOBReader": "readonly", + "ReadableStreamBYOBRequest": "readonly", + "ReadableStreamDefaultController": "readonly", + "ReadableStreamDefaultReader": "readonly", + "Request": "readonly", + "Response": "readonly", + "setImmediate": "readonly", + "setInterval": "readonly", + "setTimeout": "readonly", + "structuredClone": "readonly", + "SubtleCrypto": "readonly", + "SuppressedError": "readonly", + "TextDecoder": "readonly", + "TextDecoderStream": "readonly", + "TextEncoder": "readonly", + "TextEncoderStream": "readonly", + "TransformStream": "readonly", + "TransformStreamDefaultController": "readonly", + "URL": "readonly", + "URLPattern": "readonly", + "URLSearchParams": "readonly", + "WebAssembly": "readonly", + "WebSocket": "readonly", + "WritableStream": "readonly", + "WritableStreamDefaultController": "readonly", + "WritableStreamDefaultWriter": "readonly" + }, + "env": { + "es2026": true + } + }, + { + "files": [ + "test/unit/**/*.js" + ], + "env": { + "vitest": true + } + } + ] +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 58d16df6a6..3df20a4cfe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,15 +39,15 @@ For example but not limited to: - **Processing (Java)** → [Processing repository](https://github.com/processing/processing4) - **p5.js Web Editor** → [p5.js Web Editor repository](https://github.com/processing/p5.js-web-editor) - **p5.js Website** → [p5.js Website repository](https://github.com/processing/p5.js-website) -- **OpenProcessing.org** → [OpenProcessing website](https://openprocessing.org/) *(Note: OpenProcessing is not affiliated with the Processing Foundation and not an Open Source project)* +- **OpenProcessing.org** → [OpenProcessing website](https://openprocessing.org/) _(Note: OpenProcessing is not affiliated with the Processing Foundation and not an Open Source project)_ - **Help with a project** → [Processing Forum](https://discourse.processing.org/), [p5.js Discord](https://discord.p5js.org) -For Example: +For Example: > It sounds like you're interested in contributing to Processing (Java)! That work lives in a separate repository at https://github.com/processing/processing4. This repository is the p5.js library codebase, which is a different project. -> +> > For your specific interests in Processing, the right place to explore and contribute is: -> +> > - Processing repository: https://github.com/processing/processing4 > - Processing contribution guidelines: https://github.com/processing/processing4/blob/main/CONTRIBUTING.md > @@ -73,7 +73,7 @@ You are a guide, not a gatekeeper. Assume good faith, always, and before anythin ### How to talk with people -Talk with contributors, not at them. Ask natural questions to understand where they are and what they need. A contributor should leave the conversation feeling more confident and more connected, not evaluated. Be conversational. You are not a compliance system and the contributor is not a form to be processed. Talk with them, not at them. +Talk with contributors, not at them. Ask natural questions to understand where they are and what they need. A contributor should leave the conversation feeling more confident and more connected, not evaluated. Be conversational. You are not a compliance system and the contributor is not a form to be processed. Talk with them, not at them. ### How to ask question @@ -86,7 +86,7 @@ Ask one question at a time, and give the contributor a chance to respond before - DO NOT post walls of text or long lists of instructions - DO NOT use — em dashes - en dashes, -- double dashes, or other separators to break up text. Use paragraphs and natural language instead. - DO NOT overuse bullet points, or numbered lists. -- DO NOT position yourself as an authority +- DO NOT position yourself as an authority - DO NOT be patronizing - DO NOT suggest that someone needs to meet certain criteria to belong here - DO NOT preach, recite the guidelines back to the user, or make them feel like they are being tested or policed @@ -99,9 +99,9 @@ A good interaction is not one where the contributor followed all the steps corre --- -## When to help +## When to help -The best time to help is before any code is written. +The best time to help is before any code is written. That is when the contributor is most open to guidance, and when your guidance can have the most impact. @@ -109,15 +109,15 @@ Do not fetch issue details, scan the codebase, check assignments, or start writi ## How to help -### 0. Welcome them and find out where they are +### 0. Welcome them and find out where they are Because everyone who shows up deserves to be seen, begin by genuinely orienting yourself to the person in front of you before offering any guidance. Ask naturally, not as a checklist or bullet list. You might want to find out: -- Are they a first-time contributor, or have they contributed before? -- Have they used p5.js before? -- Are they new to open source, or just new to p5.js? +- Are they a first-time contributor, or have they contributed before? +- Have they used p5.js before? +- Are they new to open source, or just new to p5.js? - Do they have a specific issue in mind, or are they still looking for a way in? A simple "is this your first time contributing to p5.js?" can be a good starting point. The more you understand about where they are coming from, the better you can help them find the right way in. @@ -147,7 +147,7 @@ You WILL NOT suggest an issue that is already assigned. You MAY select an unassigned issue that seems like a good fit for the contributor, but you MUST check the discussion thread to make sure there is an agreed-upon implementation approach and that it is not already being worked on before you suggest it to the contributor. -If they are a first time contributor, you MAY also suggest issues with the "good first issue" label. +If they are a first time contributor, you MAY also suggest issues with the "good first issue" label. If they are NOT a first time contributor, DO NOT suggest a "good first issue". These are set aside specifically to help newcomers make their first contribution. @@ -155,7 +155,7 @@ If no unassigned issues are available, or if the issue they want to work on is a You SHOULD suggest that they ask in the Discord or issue threads if they are not sure how to get started. -In cases where letting the contributor explore on their own seems like the better approach, you MAY share links to ["help wanted"](https://github.com/processing/p5.js/issues?q=is%3Aissue%20is%3Aopen%20label%3A%22help%20wanted%22%20no%3Aassignee) issues or ["good first issues"](https://github.com/processing/p5.js/issues?q=is%3Aissue%20is%3Aopen%20label%3A%22good%20first%20issue%22%20no%3Aassignee) +In cases where letting the contributor explore on their own seems like the better approach, you MAY share links to ["help wanted"](https://github.com/processing/p5.js/issues?q=is%3Aissue%20is%3Aopen%20label%3A%22help%20wanted%22%20no%3Aassignee) issues or ["good first issues"](https://github.com/processing/p5.js/issues?q=is%3Aissue%20is%3Aopen%20label%3A%22good%20first%20issue%22%20no%3Aassignee) **Note on p5.js 2.0:** p5.js is actively developing version 2.0, with 1.x available on a branch but not in active development or maintenance. The git branches are currently separated with `v1` on 1.x and `main` on 2.x. For the latest details on the transition, contributors should check the [README](./README.md). For contributing ideas or implementation to 2.x, check the [2.x project board](https://github.com/orgs/processing/projects/21/views/8) for an overview of what still needs discussion and what's ready for work. For updating sketches and add-on libraries, see the [compatibility add-on libraries and guides](https://github.com/processing/p5.js-compatibility). @@ -165,29 +165,29 @@ You MUST encourage the contributor to review the issue details and discussion th A good issue for a contributor to work on is one where: -- The issue is open and not yet assigned, or is assigned to this contributor +- The issue is open and not yet assigned, or is assigned to this contributor - An implementation approach has been discussed and agreed upon with maintainers or stewards -If the issue is already assigned to someone else, let the contributor know warmly and help them find one that is available. +If the issue is already assigned to someone else, let the contributor know warmly and help them find one that is available. If no approach has been agreed upon yet, encourage them to investigate and post their thinking in the issue thread first and wait for maintainer or steward input. ### 4. Ask them to explain the issue before writing any code -Do NOT move toward implementation until the contributor can explain the issue in their own words. +Do NOT move toward implementation until the contributor can explain the issue in their own words. For example you MAY ask questions like: -- "What do you understand the issue to be about?" -- "What do you think is causing it?" +- "What do you understand the issue to be about?" +- "What do you think is causing it?" - "How were you thinking of approaching it?" If they cannot explain the issue even after exploring it, encourage them to ask in the thread or on Discord before going further. ### 5. Keep contributions small and grounded -- Encourage focused PRs the contributor can fully explain -- If someone is reaching for something larger than they understand, help them find the smaller version they can actually own +- Encourage focused PRs the contributor can fully explain +- If someone is reaching for something larger than they understand, help them find the smaller version they can actually own - Caution against refactoring or structural changes unless the contributor has a deep, established familiarity with the codebase ### 6. Encourage disclosure of AI use @@ -202,9 +202,9 @@ Code is not the only way to contribute. If a contributor is struggling to find a way in through code or doesn't feel ready to make a PR, here are other meaningful ways to contribute: -- Comment on an issue with relevant insights, questions, or suggestions -- Help others in the Discord or issue threads on the forum, especially if they have relevant and specific experience -- Helpful, friendly, and technically thorough feedback in others' PRs. Carefully picking PRs they have the technical knowledge to review and leaving a helpful comment is great, and helps make the overall development process a bit smoother for everyone! +- Comment on an issue with relevant insights, questions, or suggestions +- Help others in the Discord or issue threads on the forum, especially if they have relevant and specific experience +- Helpful, friendly, and technically thorough feedback in others' PRs. Carefully picking PRs they have the technical knowledge to review and leaving a helpful comment is great, and helps make the overall development process a bit smoother for everyone! - Testing release candidates. Particularly noting the changelog and testing the areas affected, then filing issues reporting any bugs they find. - Translation and internationalization work, including contributing to the Friendly Error System (FES) translations - Documentation, examples, and tutorials @@ -277,4 +277,4 @@ These are principles, not rules. The situations you encounter will not always be **Opportunity.** The right issue for a contributor is not the easiest one available, but the one where they can genuinely contribute something and learn in the process. A Good First Issue was set aside specifically to help a newcomer find their footing. Solving it yourself closes a door that was left open for someone else. -**Vulnerability.** Talking to an agent often feels safer than talking to people. There is less risk of judgment, less exposure. But that safety is also a ceiling. Becoming part of a community involves friction: asking questions you are embarrassed to ask, getting feedback that surprises you, becoming known to someone who can vouch for you later. An agent that shields contributors from that experience is not protecting them. It is keeping them on the outside. \ No newline at end of file +**Vulnerability.** Talking to an agent often feels safer than talking to people. There is less risk of judgment, less exposure. But that safety is also a ceiling. Becoming part of a community involves friction: asking questions you are embarrassed to ask, getting feedback that surprises you, becoming known to someone who can vouch for you later. An agent that shields contributors from that experience is not protecting them. It is keeping them on the outside. diff --git a/AI_USAGE_POLICY.md b/AI_USAGE_POLICY.md index 210446e606..15dc260fd8 100644 --- a/AI_USAGE_POLICY.md +++ b/AI_USAGE_POLICY.md @@ -12,7 +12,7 @@ **All contributions must come from humans who understand and can take full responsibility for their code.** -Large language models (LLMs) make mistakes and cannot be held accountable for their outputs. This is why we require human understanding and ownership of all submitted work. +Large language models (LLMs) make mistakes and cannot be held accountable for their outputs. This is why we require human understanding and ownership of all submitted work. > [!WARNING] > Maintainers may close PRs that appear to be fully or largely AI-generated. @@ -36,6 +36,7 @@ If you do end up using AI tools, we ask that you only do so **assistively** (lik 5. **Follow guidelines:** Comply with all rules in [AGENTS.md](./AGENTS.md) and [CONTRIBUTING.md](./CONTRIBUTING.md) ### Example disclosure + > I used Claude to help debug a test failure. I reviewed the suggested fix, tested it locally, and verified it solves the issue without side effects. > I used ChatGPT to help me understand an error message and suggest debugging steps. I implemented the fix myself after verifying it. @@ -43,6 +44,7 @@ If you do end up using AI tools, we ask that you only do so **assistively** (lik ## What AI Tools Can Do ✅ **Allowed (assistive use):** + - Explain concepts or existing code - Suggest debugging approaches - Help you understand error messages @@ -53,6 +55,7 @@ If you do end up using AI tools, we ask that you only do so **assistively** (lik ## What AI Tools Cannot Do ❌ **Not allowed (generative use):** + - Write entire PRs or large code blocks - Make implementation decisions for you - Submit code you don't understand @@ -60,9 +63,10 @@ If you do end up using AI tools, we ask that you only do so **assistively** (lik - Automate the submission of code changes ## Why do we have this policy? + AI-based coding assistants are increasingly enabled by default at every step of the contribution process, and new contributors are bound to encounter them and use them in good faith. -While these tools can help newcomers navigate the codebase, they often generate well-meaning but unhelpful submissions. +While these tools can help newcomers navigate the codebase, they often generate well-meaning but unhelpful submissions. There are also ethical and legal considerations around authorship, licensing, and environmental impact. @@ -74,7 +78,7 @@ The [AGENTS.md](./AGENTS.md) file contains instructions for AI coding assistants Note that [AGENTS.md](./AGENTS.md) is intentionally structured so that large language models (LLMs) can better comply with the guidelines. This explains why certain sections may seem redundant, overly directive or repetitive. -This is not a perfect solution. Agents may ignore it or be convinced to generate code anyway. However, this is our best effort to guide their behavior and encourage responsible use. +This is not a perfect solution. Agents may ignore it or be convinced to generate code anyway. However, this is our best effort to guide their behavior and encourage responsible use. We are continuously looking for ways to improve our approach and may have to change our policies as AI tools evolve. We welcome feedback and suggestions from the community. @@ -87,4 +91,4 @@ If you're unsure whether your use of AI tools complies with this policy, ask in ## AI Disclosure -This policy was created with the assistance of AI tools, including ChatGPT and Claude. It was thoroughly reviewed and edited by human contributors to ensure clarity and accuracy. \ No newline at end of file +This policy was created with the assistance of AI tools, including ChatGPT and Claude. It was thoroughly reviewed and edited by human contributors to ensure clarity and accuracy. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3b4567712f..9698c6c902 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,3 @@ - ## [p5.js community statement](https://p5js.org/community/) p5.js is a community interested in exploring the creation of art and design with technology. @@ -8,45 +7,48 @@ We are a community of, and in solidarity with, people from every gender identity We like these hashtags: #noCodeSnobs (because we value community over efficiency), #newKidLove (because we all started somewhere), #unassumeCore (because we don't assume knowledge), and #BlackLivesMatter (because of course). In practice: -* We are not code snobs. We do not assume knowledge or imply there are things that somebody should know. -* We insist on actively engaging with requests for feedback regardless of their complexity. -* We welcome newcomers and prioritize the education of others. We strive to approach all tasks with the enthusiasm of a newcomer. Because we believe that newcomers are just as valuable in this effort as experts. -* We consistently make the effort to actively recognize and validate multiple types of contributions. -* We are always willing to offer help or guidance. + +- We are not code snobs. We do not assume knowledge or imply there are things that somebody should know. +- We insist on actively engaging with requests for feedback regardless of their complexity. +- We welcome newcomers and prioritize the education of others. We strive to approach all tasks with the enthusiasm of a newcomer. Because we believe that newcomers are just as valuable in this effort as experts. +- We consistently make the effort to actively recognize and validate multiple types of contributions. +- We are always willing to offer help or guidance. In times of conflict: -* We listen. -* We clearly communicate while acknowledging other's feelings. -* We admit when we're wrong, apologize, and accept responsibility for our actions. -* We are continuously seeking to improve ourselves and our community. -* We keep our community respectful and open. -* We make everyone feel heard. -* We are mindful and kind in our interactions. + +- We listen. +- We clearly communicate while acknowledging other's feelings. +- We admit when we're wrong, apologize, and accept responsibility for our actions. +- We are continuously seeking to improve ourselves and our community. +- We keep our community respectful and open. +- We make everyone feel heard. +- We are mindful and kind in our interactions. In the future: -* The future is now. +- The future is now. ## p5.js Code of Conduct -* **Be mindful of your language.** Any of the following behavior is unacceptable: - * Offensive comments related to gender identity and expression, sexual orientation, race, ethnicity, language, neuro-type, size, ability, class, religion, culture, subculture, political opinion, age, skill level, occupation, or background - * Threats of violence - * Deliberate intimidation - * Sexually explicit or violent material that is not contextualized and preceded by a considerate warning - * Unwelcome sexual attention - * Stalking or following - * Or any other kinds of harassment +- **Be mindful of your language.** Any of the following behavior is unacceptable: + - Offensive comments related to gender identity and expression, sexual orientation, race, ethnicity, language, neuro-type, size, ability, class, religion, culture, subculture, political opinion, age, skill level, occupation, or background + - Threats of violence + - Deliberate intimidation + - Sexually explicit or violent material that is not contextualized and preceded by a considerate warning + - Unwelcome sexual attention + - Stalking or following + - Or any other kinds of harassment Use your best judgement. If it will possibly make others uncomfortable, do not post it. -* **Be respectful.** Disagreement is not an opportunity to attack someone else's thoughts or opinions. Although views may differ, remember to approach every situation with patience and care. -* **Be considerate.** Think about how your contribution will affect others in the community. -* **Be open minded.** Embrace new people and new ideas. Our community is continually evolving and we welcome positive change. +- **Be respectful.** Disagreement is not an opportunity to attack someone else's thoughts or opinions. Although views may differ, remember to approach every situation with patience and care. +- **Be considerate.** Think about how your contribution will affect others in the community. +- **Be open minded.** Embrace new people and new ideas. Our community is continually evolving and we welcome positive change. If you believe someone is violating the code of conduct, we ask that you report it by emailing [hello@p5js.org](mailto:hello@p5js.org). Please include your name and a description of the incident, and we will get back to you ASAP. Sometimes, participants violating the Code of Conduct are unaware that their behavior is harmful, and an open conversation clears things up to move forward. However, if a participant continues with the behavior, the p5.js team may take any action they deem appropriate, up to and including expulsion from all p5.js spaces and identification of the participant as a harasser to other p5.js members or the general public. --- + This statement is licensed under a [Creative Commons license](https://creativecommons.org/licenses/by-sa/4.0/). Please feel free to share and remix with attribution. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bcc1ebd9bf..f876fcd21d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ The GitHub issues are for bugs and feature requests for the p5.js library itself Please make sure you are posting to the correct repository. See this [section](https://github.com/processing/p5.js/blob/main/README.md#issues) for a list of all p5.js repositories. -Please be sure to review our [community statement](https://p5js.org/about/#community-statement) and [code of conduct](https://github.com/processing/p5.js/blob/main/CODE_OF_CONDUCT.md). These things are very important to us. +Please be sure to review our [community statement](https://p5js.org/about/#community-statement) and [code of conduct](https://github.com/processing/p5.js/blob/main/CODE_OF_CONDUCT.md). These things are very important to us. Check out the [contributor docs](https://p5js.org/contribute/) for more in-depth details about contributing code, bug fixes, and documentation. @@ -13,26 +13,33 @@ Check out the [contributor docs](https://p5js.org/contribute/) for more in-depth The following are some key points from our [contributor guidelines](https://p5js.org/contribute/contributor_guidelines/). Please read the full document for more details. ### Get Assigned Before Working on an Issue + You should not “jump the queue” by filing a PR for an issue that either someone else has indicated willingness to submit a contribution or has already been assigned to someone else. We will always prioritize the “first assigned, first serve” order for accepting code contributions for an issue. If you file a PR for an issue while someone else is still working on the same issue, your PR will likely be closed. ### You may follow up on Stalled Issues + If you see that it has been a few weeks since the last activity on an issue with an assigned individual, you can leave a polite comment on the issue asking for progress and if they need help with the implementation. We generally allow for people to work on their contributions at their own pace, as we understand that most people will often be working on a volunteer basis, or it simply takes more time for them to work on the feature. ### Only Issues Approved for Implementation May Be Worked On + You should not file a pull request (or start working on code changes) without a corresponding issue or before an issue has been approved for implementation, that is because there is no guarantee that the proposal will be accepted. Any pull requests filed before a proposal has been approved will be closed until approval is given to the issue. ### Include Unit Tests + Add any unit tests if you are working on adding new features or feature enhancement. Frequently run `npm test` and make sure all existing and new tests pass before submitting a PR. ### Follow Code Standards + Make sure your code follows the established code standards for p5.js. Any git commit and pull request must pass linting before it will be accepted. The easiest way for you to follow the right coding standard is to use the ESLint plugin available for your text editor with linting error highlighting (available for most popular text editors). ### Preparing Pull Requests + After making the changes you need to make, including unit tests if applicable, `npm test` does not error, and you have committed the changes, you can start preparing a pull request (PR) to get your new commits merged into the official p5.js repository. A pull request, more formally, is a request to a repo (in this case, the official p5.js repo) to pull or merge changes from another repo (in this case, your forked p5.js repo) into its commit history. ## AI Usage Policy -This project does *not* accept fully AI-generated contributions. AI tools may be used assistively only. As a contributor, you should be able to understand and take responsibility for changes you make to the codebase. + +This project does _not_ accept fully AI-generated contributions. AI tools may be used assistively only. As a contributor, you should be able to understand and take responsibility for changes you make to the codebase. Agents and AI coding assistants must follow the guidelines in `./AGENTS.md` and read the full [contributor_guidelines.mdx](https://raw.githubusercontent.com/processing/p5.js-website/main/src/content/contributor-docs/en/contributor_guidelines.mdx). -Please read the [AI Usage Policy](./AI_USAGE_POLICY.md) and [AGENTS.md](./AGENTS.md) before proceeding. \ No newline at end of file +Please read the [AI Usage Policy](./AI_USAGE_POLICY.md) and [AGENTS.md](./AGENTS.md) before proceeding. diff --git a/README.md b/README.md index baae662b0b..06fd57d20c 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,12 @@ p5.js is a free and open-source JavaScript library for [accessible](https://p5js ```js function setup() { - createCanvas(400, 400); - background(255); + createCanvas(400, 400); + background(255); } function draw() { - circle(mouseX, mouseY, 80); + circle(mouseX, mouseY, 80); } ``` @@ -61,7 +61,8 @@ p5.js is a collaborative project with many contributors, mostly volunteers, and A quick Getting Started with the Build and setting up the repository could be found [here](https://p5js.org/contribute/contributor_guidelines/#quick-get-started-for-developers). ## AI Usage Policy -This project does *not* accept fully AI-generated contributions. AI tools may be used assistively only. As a contributor, you should be able to understand and take responsibility for changes you make to the codebase. + +This project does _not_ accept fully AI-generated contributions. AI tools may be used assistively only. As a contributor, you should be able to understand and take responsibility for changes you make to the codebase. More details can be found in our [AI Usage Policy](./AI_USAGE_POLICY.md) and [AGENTS.md](./AGENTS.md). @@ -74,35 +75,39 @@ Anyone interested can volunteer to be a steward! There are no specific requireme p5.js was created by [Lauren Lee McCarthy](https://github.com/lmccart) in 2013 as a new interpretation of Processing for the context of the web. Since then we have allowed ourselves space to deviate and grow, while drawing inspiration from Processing and our shared community. p5.js is sustained by a community of contributors, with support from the Processing Foundation. p5.js follows a rotating leadership model started in 2020. Learn more about the [people](https://p5js.org/people/) behind p5.js. Current Lead/Mentor -* [@ksen0](https://github.com/ksen0) - p5.js Lead,2024-present -* [@limzykenneth](https://github.com/limzykenneth) - p5.js Mentor,2023-present + +- [@ksen0](https://github.com/ksen0) - p5.js Lead,2024-present +- [@limzykenneth](https://github.com/limzykenneth) - p5.js Mentor,2023-present Lead/Mentor Alumni -* [@lmccart](https://github.com/lmccart) - p5.js Creator -* [@qianqianye](https://github.com/qianqianye) - p5.js Lead,2021-2025 -* [@outofambit](https://github.com/outofambit) - p5.js Co-Lead 2021-22, Mentor 2022-2023 -* [@mcturner1995](https://github.com/mcturner1995) - p5.js Lead 2020 + +- [@lmccart](https://github.com/lmccart) - p5.js Creator +- [@qianqianye](https://github.com/qianqianye) - p5.js Lead,2021-2025 +- [@outofambit](https://github.com/outofambit) - p5.js Co-Lead 2021-22, Mentor 2022-2023 +- [@mcturner1995](https://github.com/mcturner1995) - p5.js Lead 2020 -| Area | Steward(s) | -|------|-------------| -| Maintainers | [@davepagurek](https://github.com/davepagurek), [@doradocodes](https://github.com/doradocodes), [@ksen0](https://github.com/ksen0), [@limzykenneth](https://github.com/limzykenneth), [@perminder-17](https://github.com/perminder-17) | -| Accessibility | [@calebfoss](https://github.com/calebfoss) | -| Accessibility (p5.js-website) | [@coseeian](https://github.com/coseeian) | -| Color | [@limzykenneth](https://github.com/limzykenneth) | -| Core | [@davepagurek](https://github.com/davepagurek) | -| DevOps | [@Vaivaswat2244](https://github.com/Vaivaswat2244), [@limzykenneth](https://github.com/limzykenneth), [@lirenjie95](https://github.com/lirenjie95) | -| Documentation | [@limzykenneth](https://github.com/limzykenneth), [@perminder-17](https://github.com/perminder-17) | -| Graphics (WebGL) | [@RandomGamingDev](https://github.com/RandomGamingDev), [@davepagurek](https://github.com/davepagurek), [@perminder-17](https://github.com/perminder-17) | -| Graphics (WebGPU) | [@aashu2006](https://github.com/aashu2006), [@davepagurek](https://github.com/davepagurek), [@perminder-17](https://github.com/perminder-17) | -| i18n (es) | [@marioguzzzman](https://github.com/marioguzzzman) | -| i18n (hi) | [@Divyansh013](https://github.com/Divyansh013) | -| i18n (ko) | [@eupthere](https://github.com/eupthere), [@JiwonHaam](https://github.com/JiwonHaam) | -| i18n (zh) | [@limzykenneth](https://github.com/limzykenneth), [@lirenjie95](https://github.com/lirenjie95) | -| p5.js-web-editor | [@kitlord](https://github.com/kitlord) | -| p5.js-website | [@Nwakaego-Ego](https://github.com/Nwakaego-Ego), [@clairep94](https://github.com/clairep94), [@doradocodes](https://github.com/doradocodes), [@ksen0](https://github.com/ksen0) | -| p5.sound.js | [@ogbabydiesal](https://github.com/ogbabydiesal) | -| Typography | [@dhowe](https://github.com/dhowe) | + +| Area | Steward(s) | +| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Maintainers | [@davepagurek](https://github.com/davepagurek), [@doradocodes](https://github.com/doradocodes), [@ksen0](https://github.com/ksen0), [@limzykenneth](https://github.com/limzykenneth), [@perminder-17](https://github.com/perminder-17) | +| Accessibility | [@calebfoss](https://github.com/calebfoss) | +| Accessibility (p5.js-website) | [@coseeian](https://github.com/coseeian) | +| Color | [@limzykenneth](https://github.com/limzykenneth) | +| Core | [@davepagurek](https://github.com/davepagurek) | +| DevOps | [@Vaivaswat2244](https://github.com/Vaivaswat2244), [@limzykenneth](https://github.com/limzykenneth), [@lirenjie95](https://github.com/lirenjie95) | +| Documentation | [@limzykenneth](https://github.com/limzykenneth), [@perminder-17](https://github.com/perminder-17) | +| Graphics (WebGL) | [@RandomGamingDev](https://github.com/RandomGamingDev), [@davepagurek](https://github.com/davepagurek), [@perminder-17](https://github.com/perminder-17) | +| Graphics (WebGPU) | [@aashu2006](https://github.com/aashu2006), [@davepagurek](https://github.com/davepagurek), [@perminder-17](https://github.com/perminder-17) | +| i18n (es) | [@marioguzzzman](https://github.com/marioguzzzman) | +| i18n (hi) | [@Divyansh013](https://github.com/Divyansh013) | +| i18n (ko) | [@eupthere](https://github.com/eupthere), [@JiwonHaam](https://github.com/JiwonHaam) | +| i18n (zh) | [@limzykenneth](https://github.com/limzykenneth), [@lirenjie95](https://github.com/lirenjie95) | +| p5.js-web-editor | [@kitlord](https://github.com/kitlord) | +| p5.js-website | [@Nwakaego-Ego](https://github.com/Nwakaego-Ego), [@clairep94](https://github.com/clairep94), [@doradocodes](https://github.com/doradocodes), [@ksen0](https://github.com/ksen0) | +| p5.sound.js | [@ogbabydiesal](https://github.com/ogbabydiesal) | +| Typography | [@dhowe](https://github.com/dhowe) | + ## Contributors diff --git a/SECURITY.md b/SECURITY.md index d8d4bb5fcb..b264a2624b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -5,11 +5,13 @@ Please report security vulnerabilities by emailing: responsible.disclosure@processingfoundation.org ## What to Expect + - We will acknowledge your email within 72 hours - We will provide regular updates about our progress - Once the issue is confirmed and fixed, we may ask you to verify the solution ## Disclosure Policy + - Please do not disclose the vulnerability publicly until we have had a chance to address it - We do not offer bounties as we are a non-profit organization - We appreciate your efforts to responsibly disclose your findings diff --git a/contributor_docs/README.md b/contributor_docs/README.md index f48328af37..603e8d9e07 100644 --- a/contributor_docs/README.md +++ b/contributor_docs/README.md @@ -3,12 +3,15 @@ Thanks for your interest in contributing to p5.js! p5.js is a collaborative project with contributions from many volunteers. Our community is always looking for contributors and appreciates involvement in all forms. We acknowledge that not everyone has the capacity, time, or financial means to participate actively or in the same ways. We want to expand the meaning of the word “contributor.” Whether you're an experienced developer or just starting out, we value your involvement. Your unique perspectives, skills, and experiences enrich our community, and we encourage you to get involved in a way that works for you. It includes documentation, teaching, writing code, making art, writing, design, activism, organizing, curating, or anything else you might imagine. Our [contribute page](https://p5js.org/contribute/) gives an overview of different ways to get involved and contribute. p5.js project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. We use the @all-contributors bot to handle adding people to the README.md file. You can ask @all-contributors bot to add you in an issue or PR comment like so: + ``` @all-contributors please add @[your GitHub handle] for [your contribution type] ``` + You can find relevant contribution type [here](https://allcontributors.org/docs/en/emoji-key). Although we will usually automatically add you to the contributor list using the bot after merging your PR. The contributor docs are published on p5.js [website](https://p5js.org/contribute/), and hosted on p5.js [GitHub repository](https://github.com/processing/p5.js/tree/main/contributor_docs). # Before Contributing + Contributing to p5.js should be a stress free experience and we welcome contributions of all levels, whether you are just fixing a small typo in the documentation or refactoring complex 3D rendering functionalities. However there are just a few things you should be familiar with before starting your contribution. First, please have a read through our [community statement](https://p5js.org/about/#community-statement). @@ -16,14 +19,18 @@ First, please have a read through our [community statement](https://p5js.org/abo Next, we are currently prioritizing work that expands access (inclusion and accessibility) to p5.js! See [our access statement](./access.md) for more details. # Get Started + Now you are ready to start contributing to p5.js! There are many ways to get started with contributing to p5.js and many reasons to do so. For the purpose of this documentation, we will split contributions roughly into two categories. + - Source code contribution (including documentation) - Non-source code contribution Depending on what kind of contribution you are making to p5.js, please read on to the relevant section of this documentation. ## [Source code contribution](./contributor_guidelines.md) + For a typical contribution to the p5.js or p5.js-website repository, we will follow the following steps: + 1. Open an issue 2. Discuss 3. Approved for opening a Pull Request (PR) @@ -39,6 +46,7 @@ Most of the time we will stick with this workflow quite strictly and, especially **We see contributing to p5.js as a learning opportunity** and we don't measure success by only looking at the volume of contributions we received. There is no time limit on how long it takes you to complete a contribution, so take your time and work at your own pace (we may check in after a long period of inactivity). Ask for help from any of the stewards or maintainers if you need them and we'll try our best to support you. For information related to area stewards or general maintenance of p5.js GitHub repository, please check out the [steward guidelines](./steward_guidelines.md). ## Non-source code contribution + There are many more ways to contribute to p5.js through non-source code contribution than can be exhaustively listed here. Some of the ways may also involve working with some of the p5.js repositories (such as adding examples, writing tutorials for the website, etc.). Depending on what the planned contribution is, we may be able to support you in different ways so do reach out to us via any channel available to you (email, social media, [Discourse forum](https://discourse.processing.org/c/p5js/10), Discord, etc). Here are just some ways you can contribute: **Create.** Inspire others with your sketches. p5.js is looking for designers, artists, coders, and programmers to showcase their creative, amazing work on the community sketch gallery. Don’t forget to tag @p5xjs on [Instagram](https://www.instagram.com/p5xjs/) and [X](https://twitter.com/p5xjs/), and we will do our best to share what you're doing. diff --git a/contributor_docs/access.md b/contributor_docs/access.md index 7fcac44946..2835612454 100644 --- a/contributor_docs/access.md +++ b/contributor_docs/access.md @@ -11,7 +11,7 @@ Increasing access is not focused on expanding the raw number of people in the p5 Access here means making p5.js equitable for: - People who speak languages other than English -- Black, Indigenous, People of Color, and people of marginalized ethnicities +- Black, Indigenous, People of Color, and people of marginalized ethnicities - Lesbian, gay, bisexual, queer, questioning, pansexual, and asexual people - Trans, genderfluid, agender, intersex, and two-spirit people, women, and others with marginalized genders - People who are blind, d/Deaf[^2] or hard of hearing, disabled/have a disability, neurodivergent, and chronically ill[^3] @@ -26,8 +26,8 @@ Access here means making p5.js equitable for: We recognize the complexity of the terms used to describe our respective identities. Language is nuanced, evolving, and contested. This is not an exhaustive list. We provide an attempt to name and be accountable to our commitments and to the diverse needs of the p5.js community. - ### Examples + These are examples of efforts we believe increase access: - Translating documentation and other materials into more languages, decentering linguistic imperialism[^4] (e.g., Rolando Vargas’ [Processing in Kuna Language](https://medium.com/@ProcessingOrg/culture-as-translation-processing-in-kuna-language-with-rolando-vargas-and-edinson-izquierdo-8079f14851f7), Felipe Santos Gomes, Julia Brasil, Katherine Finn Zander, and Marcela Mancino’s [Pê Cinco: Internationalization and Popularization for Portuguese Speakers](https://medium.com/processing-foundation/translating-p5-js-into-portuguese-for-the-brazilian-community-14b969e77ab1)) @@ -39,20 +39,22 @@ These are examples of efforts we believe increase access: - Supporting the creation of educational resources (e.g., Adekemi Sijuwade-Ukadike’s [A11y Syllabus](http://a11ysyllabus.site/)) - Publishing documentation and reports of our work that follow WCAG guidelines, use plain language, and focus on beginners from diverse experiences (e.g., [OSACC p5.js Access Report](https://github.com/processing/OSACC-p5.js-Access-Report)) - - ## Maintenance + We are not accepting feature requests that do not support our effort to increase access. You'll see this criteria reflected in our issue and pull request templates. We also affirm our intention to maintain the existing feature set of p5.js. We'd like to fix bugs regardless of which area of the codebase they're in. We believe consistency of the tool makes it more accessible for beginners. Examples of feature requests that improve accessibility include: Performance increases for people using less powerful hardware (e.g., Support for drawing to/reading from framebuffers) Consistency in the API (e.g, Add arcVertex() for creating arcs with beginShape()/endShape()) -___ +--- Please consider this a ‘living document.' We will continue the conversation about what it means to prioritize access. We invite our community to engage with this document and the values it describes. If you have ideas or suggestions, we invite you to share them as an issue on Github or by emailing hello@p5js.org. -This version of the p5.js Access Statement was revised in collaboration with Evelyn Masso, Nat Decker, Bobby Joe Smith III, Sammie Veeler, Sonia (Suhyun) Choi, Xin Xin, Kate Hollenbach, Lauren Lee McCarthy, Caroline Sinders, Qianqian Ye, Tristan Jovani Magno Espinoza, Tanvi Sharma, Tsige Tafesse, and Sarah Ciston at the 2023 Open Source Arts Contributors Conference. It was finalized and published by Bobby Joe Smith III and Nat Decker through the support of the Processing Foundation Fellowship. +This version of the p5.js Access Statement was revised in collaboration with Evelyn Masso, Nat Decker, Bobby Joe Smith III, Sammie Veeler, Sonia (Suhyun) Choi, Xin Xin, Kate Hollenbach, Lauren Lee McCarthy, Caroline Sinders, Qianqian Ye, Tristan Jovani Magno Espinoza, Tanvi Sharma, Tsige Tafesse, and Sarah Ciston at the 2023 Open Source Arts Contributors Conference. It was finalized and published by Bobby Joe Smith III and Nat Decker through the support of the Processing Foundation Fellowship. [^1]: Crenshaw, Kimberlé (1989). "Demarginalizing the intersection of race and sex: a black feminist critique of antidiscrimination doctrine, feminist theory and antiracist politics". University of Chicago Legal Forum. 1989 (1): 139–167. ISSN 0892-5593. Full text at Archive.org. -[^2]: Capital ‘D’ Deaf refers to people who are culturally Deaf or part of the Deaf community while lower case ‘d’ deaf is an audiological term that can describe people not associated with Deaf identity. + +[^2]: Capital ‘D’ Deaf refers to people who are culturally Deaf or part of the Deaf community while lower case ‘d’ deaf is an audiological term that can describe people not associated with Deaf identity. + [^3]: There are differing preferences between ‘person-first’ vs. ‘identity-first’ language within the disability community. Read [Unpacking the debate over person-first vs. identity-first language in the autism community](https://news.northeastern.edu/2018/07/12/unpacking-the-debate-over-person-first-vs-identity-first-language-in-the-autism-community/), [Disability-Affirming Language: Person-First versus Identity-First Language](https://editorstorontoblog.com/2024/02/23/disability-affirming-language-person-first-versus-identity-first-language/), and [Person-First and Identity-First Language Glossary](https://ogs.ny.gov/system/files/documents/2024/02/person-first-and-identity-first-glossary_english_final.pdf). -[^4]: Linguistic Imperialism, or Language Imperialism, refers to the ongoing domination/prioritization/imposition of certain languages such as English at the expense of native languages due to imperial expansion and globalization. + +[^4]: Linguistic Imperialism, or Language Imperialism, refers to the ongoing domination/prioritization/imposition of certain languages such as English at the expense of native languages due to imperial expansion and globalization. diff --git a/contributor_docs/ar/README.md b/contributor_docs/ar/README.md index 7379dfd971..3381ad6fac 100644 --- a/contributor_docs/ar/README.md +++ b/contributor_docs/ar/README.md @@ -1,18 +1,22 @@ # 🌸مرحبا 🌺 -شكرا لإهتمامك بالمساهمة في p5.js! مجتمعنا يقدر المساهمات بكل أشكالها ويسعى إلى توسيع معنى كلمة "مساهم/ة" بأقصى شكل ممكن؛ بشكل يتضمن توثيق البرمجيات، التدريس، كتابة الشيفرة البرمجية، صناعة الفن، الكتابة، التصميم، النشاطية، التنظيم، نَظْم المحتوى أو أي شيء أخر يمكن أن تتخيله. [صفحة المجتمع خاصتنا](https://p5js.org/community/#contribute) تعطي ملخصا للطرق المختلفة التي يمكن بها المشاركة والمساهمة معنا. -هذا المشروع يتبع المواصفات القياسية لمشروع [all-contributors "كل المساهمين"](https://github.com/kentcdodds/all-contributors). نحن نستعمل البوت @all-contributors كي نضيف المساهمين/ات إلى الملف README.md. يمكنك الطلب من البوت @all-contributors أن يضيفك عن طريق كتابة الآتي في تعليق على طلب مشكلة "issue" على جيت هب "GitHub" أو طلب سحب"pull request" : +شكرا لإهتمامك بالمساهمة في p5.js! مجتمعنا يقدر المساهمات بكل أشكالها ويسعى إلى توسيع معنى كلمة "مساهم/ة" بأقصى شكل ممكن؛ بشكل يتضمن توثيق البرمجيات، التدريس، كتابة الشيفرة البرمجية، صناعة الفن، الكتابة، التصميم، النشاطية، التنظيم، نَظْم المحتوى أو أي شيء أخر يمكن أن تتخيله. [صفحة المجتمع خاصتنا](https://p5js.org/community/#contribute) تعطي ملخصا للطرق المختلفة التي يمكن بها المشاركة والمساهمة معنا. + +هذا المشروع يتبع المواصفات القياسية لمشروع [all-contributors "كل المساهمين"](https://github.com/kentcdodds/all-contributors). نحن نستعمل البوت @all-contributors كي نضيف المساهمين/ات إلى الملف README.md. يمكنك الطلب من البوت @all-contributors أن يضيفك عن طريق كتابة الآتي في تعليق على طلب مشكلة "issue" على جيت هب "GitHub" أو طلب سحب"pull request" : + ``` @all-contributors please add @[اسم المسخدم خاصتك على جيت هب] for [نوع مساهمتك] ``` + يمكنك معرفة نوع مساهمتك المناسب من [هنا](https://allcontributors.org/docs/en/emoji-key)، على الرغم من أننا غالبا سنضيفك بشكل تلقائي إلى قائمة المساهمين بعد قبول طلب السحب خاصتك. دليل المساهمين منشور على [موقع](https://p5js.org/contributor-docs/#/) p5.js ومُستضاف على[ مستودع "repository" جيت هب](https://github.com/processing/p5.js/tree/main/contributor_docs) الخاص بـ p5.js. # قبل المساهمة معنا -المساهمة في مشروع p5.js يجب أن تكون تجربة خالية من أي ضغط، فنحن نرحب بكل المساهمات بغض النظر عن حجمها، سواء كانت إصلاح خطأ مطبعي بسيط أو إعادة هيكلة الآليات المعقدة لعرض الرسوم ثلاثية الأبعاد، لكن توجد بعض الأشياء التي يجب معرفتها وفهمها قبل البدء في المساهمة. + +المساهمة في مشروع p5.js يجب أن تكون تجربة خالية من أي ضغط، فنحن نرحب بكل المساهمات بغض النظر عن حجمها، سواء كانت إصلاح خطأ مطبعي بسيط أو إعادة هيكلة الآليات المعقدة لعرض الرسوم ثلاثية الأبعاد، لكن توجد بعض الأشياء التي يجب معرفتها وفهمها قبل البدء في المساهمة. بدايةً: من فضلك قم/قومي بقراءة [بيان مجتمعنا](https://p5js.org/community/) -أما بعد: نحن حاليا نعطي الأولوية للأعمال التي تُزيد الوصول (إمكانية الوصول والإتاحة وحقوق المعاقين/ات) لـ p5.js! للمزيد من التفاصيل، يمكنك إلقاء نظرة على [بيان الإتاحة خاصتنا](./access.md) +أما بعد: نحن حاليا نعطي الأولوية للأعمال التي تُزيد الوصول (إمكانية الوصول والإتاحة وحقوق المعاقين/ات) لـ p5.js! للمزيد من التفاصيل، يمكنك إلقاء نظرة على [بيان الإتاحة خاصتنا](./access.md) # البدء @@ -26,7 +30,8 @@ على حسب نوع المساهمة المراد القيام بها إلى p5.js، يمكنك قراءة الجزء المناسب من هذا الدليل. ## [المساهمات إلى الشيفرة المصدرية](./contributor_guidelines.md) -للقيام بمساهمة مثالية إلى مستودع p5.js أو موقع p5.js، يجب إتباع الخطوات الآتية: + +للقيام بمساهمة مثالية إلى مستودع p5.js أو موقع p5.js، يجب إتباع الخطوات الآتية:
* ...
@@ -277,32 +276,35 @@ Here's a cut-down example from the source of [`applyMatrix`](https://github.com/
### `@method` - specifying the function name
-[`@method`](https://jsdoc.app/tags-function) is used to define the name of the function, in this case `sin`. Note that the function name does not include the brackets `()`.
+[`@method`](https://jsdoc.app/tags-function) is used to define the name of the function, in this case `sin`. Note that the function name does not include the brackets `()`.
-You may sometimes see this missing from the reference. In that case JSDoc will try to infer it by looking at the name of the function from the following source code. It is recommended to include the function name using the @method tag where possible.
+You may sometimes see this missing from the reference. In that case JSDoc will try to infer it by looking at the name of the function from the following source code. It is recommended to include the function name using the @method tag where possible.
This tag is also useful when detailing multiple signatures for a function (see later).
### `@param` - specifying details of each parameter
-[`@param`](https://jsdoc.app/tags-param) is used to define the parameters or arguments that the function accepts. It is used once per parameter.
+[`@param`](https://jsdoc.app/tags-param) is used to define the parameters or arguments that the function accepts. It is used once per parameter.
-The general format for a parameter is:
+The general format for a parameter is:
```
@param {type} name Description here.
```
-For example, the `sin()` function has:
+For example, the `sin()` function has:
+
```
@param {Number} angle the angle.
```
1. Following the keyword `@param`, the _type_ of the parameter is mentioned in curly brackets `{}`. (in this case, `{Number}`)
- - This is strongly recommended but can be omitted in certain cases (notably in p5.strands).
+
+- This is strongly recommended but can be omitted in certain cases (notably in p5.strands).
2. After the type, the next _word_ (in this case, `angle`) is the name of the parameter.
- - This must exactly match the name of the relevant parameter in the source code.
+
+- This must exactly match the name of the relevant parameter in the source code.
3. After the name, the rest of the line is the description of the parameter.
@@ -315,9 +317,11 @@ If the parameter is optional, add square brackets around the name:
#### Examples of parameter types
An example from [rect()](https://p5js.org/reference/p5/rect/), where the topleft ("tl") parameter is optional:
+
```
@param {Number} [tl] optional radius of top-left corner.
```
+
An example from [createCanvas](https://p5js.org/reference/p5/createcanvas/) where a "renderer" parameter can optionally be supplied:
```
@@ -325,24 +329,27 @@ An example from [createCanvas](https://p5js.org/reference/p5/createcanvas/) wher
```
A more advanced example from [hue](https://p5js.org/reference/p5/hue/) where the color parameter can be a p5.Color object, an array of numbers, or a string:
+
```
* @param {p5.Color|Number[]|String} color
```
-**"Number" not "number". "String" not "string"**
-
-Throughout the p5.js documentation, primitive types are all capitalized: String, Number, Boolean, etc. (This is an example where our convention differs from JSDoc.)
+**"Number" not "number". "String" not "string"**
+Throughout the p5.js documentation, primitive types are all capitalized: String, Number, Boolean, etc. (This is an example where our convention differs from JSDoc.)
#### Parameters that expect arrays
-When specifying an array type, use `type[]` rather than `Array` wrappers around code examples.
\ No newline at end of file
+```
+
+- p5 v2.x no longer uses `` wrappers around code examples.
diff --git a/contributor_docs/contributor_guidelines.md b/contributor_docs/contributor_guidelines.md
index bb25cdff79..db4f149071 100644
--- a/contributor_docs/contributor_guidelines.md
+++ b/contributor_docs/contributor_guidelines.md
@@ -10,7 +10,6 @@ This is a relatively long and comprehensive document but we will try to signpost
**If you are a new contributor, you may want to start with the first section, “All about issues.” If you just want a step-by-step setup of the development process, you can look at the “Quick Get Started For Developers” section.**
-
# Table of Contents
- [All about issues](#all-about-issues)
@@ -48,12 +47,10 @@ This is a relatively long and comprehensive document but we will try to signpost
---
-
# All about issues
The majority of the activity on p5.js' GitHub repositories (repo for short) happens in issues, which is a great place to start your contribution journey.
-
## What are issues?

@@ -66,7 +63,6 @@ either on the [forum](https://discourse.processing.com) or on other platforms su
We have created easy-to-use issue templates to aid you in deciding whether a topic should be a GitHub issue or posted elsewhere!
-
## Issue templates
p5.js's issue templates make it easier for stewards and maintainers to understand and review issues. They also make it easier for you to file the relevant issue and receive a reply faster.
@@ -77,16 +73,15 @@ To file a new issue, simply go to the "Issues" tab on the p5.js repo and click o

+### ["Found a bug"](https://github.com/processing/p5.js/issues/new?assignees=&labels=Bug&projects=&template=found-a-bug.yml)
-### ["Found a bug"](https://github.com/processing/p5.js/issues/new?assignees=\&labels=Bug\&projects=\&template=found-a-bug.yml)
-
-When you encounter possible incorrect behavior in p5.js or something not behaving as described in the documentation, use [this template](https://github.com/processing/p5.js/issues/new?assignees=\&labels=Bug\&projects=\&template=found-a-bug.yml). Please note that if you are trying to debug your sketch and you think it may be a problem with your code, you should first ask on the [Discourse forum](https://discourse.processing.org) instead.
+When you encounter possible incorrect behavior in p5.js or something not behaving as described in the documentation, use [this template](https://github.com/processing/p5.js/issues/new?assignees=&labels=Bug&projects=&template=found-a-bug.yml). Please note that if you are trying to debug your sketch and you think it may be a problem with your code, you should first ask on the [Discourse forum](https://discourse.processing.org) instead.
There are a few fields for you to fill in for this template:
-1. *Most appropriate sub-area of p5.js?* - This helps us identify and respond to your issue by automatically tagging the issue with the relevant [labels](https://github.com/processing/p5.js/labels).
-2. *p5.js version* - You can find the p5.js version number in either the `
```
@@ -119,7 +133,7 @@ import trigonometry from './trigonometry.js';
import math from './math.js';
import vector from './p5.Vector.js';
-export default function(p5, fn){
+export default function (p5, fn) {
p5.registerAddon(calculation);
p5.registerAddon(noise);
p5.registerAddon(random);
@@ -128,18 +142,20 @@ export default function(p5, fn){
p5.registerAddon(vector);
}
```
+
```js
// src/math/calculation.js (redacted)
-function calculation(p5, fn){
+function calculation(p5, fn) {
fn.abs = Math.abs;
}
export default calculation;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
calculation(p5, p5.prototype);
}
```
+
```js
// src/app.js (redacted)
// math (include if to be bundled as part of p5.js)
@@ -154,6 +170,7 @@ import './math/trigonometry';
Please see relevant examples in the exploration fork for implementation.
#### ESM
+
ESM or ES Module is the current standard in JavaScript for working with modular JavaScript code. ESM are now very widely supported with all major browsers natively supporting it, all modern build tools supports or are even built around it, and Node.js have native support for it as well. p5.js 1.0's code is already written with ESM and transpiled into a UMD module. As part of the refactor mentioned in a previos section, the syntax of the internal use of ESM will be updated to match semantic usage. The main goal will be to limit cross dependencies between modules and minimize the use of side effects imports.
```js
@@ -163,7 +180,7 @@ import math from 'p5/math';
p5.registerAddon(math);
// The same instance mode syntax
-const sketch = (p => {
+const sketch = p => {
p.setup = () => {
p.createCanvas(400, 400);
console.log(p.ceil(2.1));
@@ -173,7 +190,7 @@ const sketch = (p => {
p.background(200);
p.circle(200, 200, 100);
};
-});
+};
new p5(sketch);
```
@@ -190,71 +207,79 @@ Across both formats above, one thing that was not elaborated on is the static me
---
### Libraries
+
Existing libraries should have a level of compatibility or require minimal updates to work with p5.js 2.0.
A new method of authoring libraries will be introduced that is more ergonomic. This will be through a factory function that exposes reasonable interfaces for completing the following tasks as necessary:
-* Attaching methods and properties to prototype
-* Lifecycle hooks
-* Extending internal functionalities (eg. adding a new renderer)
+
+- Attaching methods and properties to prototype
+- Lifecycle hooks
+- Extending internal functionalities (eg. adding a new renderer)
As reference, Day.js provide plugin interface in the following way:
+
```js
export default (option, dayjsClass, dayjsFactory) => {
// extend dayjs()
// e.g. add dayjs().isSameOrBefore()
- dayjsClass.prototype.isSameOrBefore = function(arguments) {}
+ dayjsClass.prototype.isSameOrBefore = function (arguments) {};
// extend dayjs
// e.g. add dayjs.utc()
- dayjsFactory.utc = arguments => {}
+ dayjsFactory.utc = arguments => {};
// overriding existing API
// e.g. extend dayjs().format()
- const oldFormat = dayjsClass.prototype.format
- dayjsClass.prototype.format = function(arguments) {
+ const oldFormat = dayjsClass.prototype.format;
+ dayjsClass.prototype.format = function (arguments) {
// original format result
- const result = oldFormat.bind(this)(arguments)
+ const result = oldFormat.bind(this)(arguments);
// return modified result
- }
-}
+ };
+};
```
+
And is used with:
+
```js
dayjs.extend(myPlugin);
```
While jQuery provides the following interface:
+
```js
-$.fn.greenify = function() {
+$.fn.greenify = function () {
this.css('color', 'green');
};
$('a').greenify();
```
+
`fn` above is just an alias to `prototype` which in essense makes jQuery's plugin system identical to what p5.js does.
p5.js plugins have some properties that are not present in the Day.js or jQuery use case. With Day.js, plugins are expected to be explicitly provided through `dayjs.extend()` while p5.js addons should have the expectations of being available immediately upon inclusion/load. jQuery plugin don't need to content with lifecycle hooks or other non-class instance related features. A p5.js addon should also have the flexibility of being imported as a ES module or included through a script tag, ie. there should be a ES module version and a UMD version ideally.
The proposed interface that a p5.js 2.0 plugin can have is as the following:
+
```js
-(function(p5){
+(function (p5) {
p5.registerAddon((p5, fn, lifecycles) => {
// `fn` being the prototype
- fn.myMethod = function(){
+ fn.myMethod = function () {
// Perform some tasks
};
// Instead of requiring register preload,
// async/await is preferred instead.
- fn.loadMyData = async function(){
+ fn.loadMyData = async function () {
// Load some data asynchronously
};
- lifecycles.presetup = function(){
+ lifecycles.presetup = function () {
// Run actions before `setup()` runs
};
- lifecycles.postdraw = function(){
+ lifecycles.postdraw = function () {
// Run actions after `draw()` runs
};
});
@@ -264,6 +289,7 @@ The proposed interface that a p5.js 2.0 plugin can have is as the following:
---
### Renderers
+
p5.js 1.0 is bundled with two renderers: 2D and WebGL. They corresponds to the HTML Canvas `2d` and `webgl` context respectively. However, there had been requests over the years to add additional renderers such as an SVG renderer or a renderer with scene graph capabilities. As the web evolve, we are also seeing new a possible standard renderer being developed, ie. [WebGPU](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API).
As currently implemented, adding a new renderer to p5.js is not an easy task which involves many parts that expects to behave differently depending on whether the current sketch is in 2D or WebGL mode.
@@ -275,11 +301,13 @@ createCanvas(400, 400, WEBGL);
The constant value to determine whether a canvas is in 2D or WEBGL mode is also not easily extendable by addon libraries.
With p5.js 2.0, the renderer system is redesigned and tweaked with a few key points.
+
1. `p5.Renderer` class which both `p5.Renderer2D` and `p5.RendererGL` classes inherit from will now act more like an abstract class that it is meant to be.
- * The `p5.Renderer` class will determine a set of basic properties and methods any renderer class inheriting from it should implement, while extra functionalities can still be implemented on top. (eg. all renderers should implement the `ellipse()` method but the WebGL renderer will also implement a `sphere()` method that 2D renderers don't need).
- * The `p5.Renderer` class should never be instantiated directly.
+ - The `p5.Renderer` class will determine a set of basic properties and methods any renderer class inheriting from it should implement, while extra functionalities can still be implemented on top. (eg. all renderers should implement the `ellipse()` method but the WebGL renderer will also implement a `sphere()` method that 2D renderers don't need).
+ - The `p5.Renderer` class should never be instantiated directly.
2. The core will not have implicit knowledge of what renderers are available. Previously the two modes supported (`P2D` and `WEBGL`) are harded coded into functions like `createCanvas()` making creating a new rendering mode difficult without also modifying core functionalities.
- * A list of renderers should be kept under the `p5.renderers` object with value being the class object of the renderer (that inherits from `p5.Renderer` class).
+ - A list of renderers should be kept under the `p5.renderers` object with value being the class object of the renderer (that inherits from `p5.Renderer` class).
+
```js
p5.renderers = {
[constants.P2D]: Renderer2D,
@@ -290,7 +318,7 @@ p5.renderers = {
For an addon library to create a new renderer to work with p5, it will need to first create a class that inherits and implements the `p5.Renderer` abstract class, then register the renderer under the `p5.renderers` object.
```js
-(function(p5){
+(function (p5) {
class MyRenderer extends p5.Renderer {
ellipse(x, y, w, h) {
// ...
@@ -308,7 +336,7 @@ For an addon library to create a new renderer to work with p5, it will need to f
When a sketch author wants to use the addon provided renderer above, they can use the following code when creating a canvas.
```js
-function setup(){
+function setup() {
createCanvas(400, 400, 'myRenderer');
}
```
@@ -316,18 +344,21 @@ function setup(){
For usage that are more similar to p5.js' own renderers, constants registration can be exposed to addon library authors as well. In this case, it is recommended to make the constant value a `Symbol` matching behavior in the core library itself.
#### Core question
+
Part of the motivation for this proposal is to enable a leaner build of the library for users who only use the 2D renderer but not the WebGL renderer and vice versa. If someone uses only the 2D canvas, there is no need for most if not all of the WebGL components to be included.
This creates a question, should p5.js still be bundled with both renderers for distribution? What about new renderers in the future? There are a few options for this:
-* Bundling both 2D and WebGL renderers, bundling also any new renderers if they are included in the library directly.
-* Bundle only 2D renderer. Log a warning message if the sketch author tries to create a WebGL canvas without loading in the WebGL renderer as an addon.
-* Bundle no renderer. All and any renderers should be included as addons.
+
+- Bundling both 2D and WebGL renderers, bundling also any new renderers if they are included in the library directly.
+- Bundle only 2D renderer. Log a warning message if the sketch author tries to create a WebGL canvas without loading in the WebGL renderer as an addon.
+- Bundle no renderer. All and any renderers should be included as addons.
The third options is probably too extreme and probably should not be considered. Either of the first two options are open for discussions.
---
### Async
+
Data loading functions will all be updated to be fully async using promises (with `async`/`await`) while keeping the callback syntax if necessary. `setup()` will be awaited in the runtime and if the user define it as `async` functions, the promisified data loading functions can be awaited in them. With an async `setup()` function, the `draw()` loop will only start after the `setup()` function has resolved.
`draw()` can be defined as an `async` function as well, although this is not recommended because of the possible timing conflict it may have with `requestAnimationFrame`.
@@ -337,7 +368,9 @@ The async `setup()` function eliminates the need to have the `preload()` functio
---
### Algorithm changes
+
#### RNG
+
Seeded random number generator (RNG) will use xorshift128+. The current algorithm for RNG used in p5.js is an version of a Linear Congruential Generator (LCG).
The main reasoning for this change is for performance. xorshift128+ performs better than LCG and it is the internal implementation of most of the current major browsers for `Math.random()`. The random property of xorshift128+ is similar if not better than LCG.
@@ -347,6 +380,7 @@ This will be a breaking change as existing seeded RNG will not give the same ran
Example implementation of a xorshift128+ backed RNG with compatible API to p5.js can be found [here](https://github.com/limzykenneth/js-xorshift128p).
#### Noise
+
`noise()` function will use [Simplex noise](https://en.wikipedia.org/wiki/Simplex_noise) instead of the current [Perlin noise](https://en.wikipedia.org/wiki/Perlin_noise). Simplex noise has better performance than Perlin noise and no noticeable directional artifacts.
Until January 8, 2022, Simplex noise is protected by a US Patent, making adopting it problematic in certain cases. The patent has since expired, opening the way for its implementation in p5.js. This will be a breaking change as existing seeded `noise()` output will not give the same output after the algorithm has switched to use Simplex noise. Consideration can be made to create a compatibility addon library that patch seeded `noise()` to use Perlin noise if necessary.
@@ -354,36 +388,44 @@ Until January 8, 2022, Simplex noise is protected by a US Patent, making adoptin
`noise()` will also accept higher dimensional input with the new implementation through accepting any number of numerical arguments passed to it (currently only accept either 2 or 3 arguments).
#### Color
+
A new color module will be implemented. The requirements of this new color module are:
-* Full compatibility with CSS color space support.
-* Possibility for addon libraries to introduce additional color spaces.
-* Accurate gamut mapping.
-* API as close to existing implementation as possible.
+
+- Full compatibility with CSS color space support.
+- Possibility for addon libraries to introduce additional color spaces.
+- Accurate gamut mapping.
+- API as close to existing implementation as possible.
#### Math
+
A new math module will be implemented. The requirements of this new math module are:
-* Unified vector and matrices support with compatible API with ml5.js.
-* Explore reasonable performance optimizations through the use of specific algorithms, GPU implementation, or others.
-* Possibility for addon libraries to provide own implementation (so that any external libraries can be made compatible without being implemented internally by p5.js).
+
+- Unified vector and matrices support with compatible API with ml5.js.
+- Explore reasonable performance optimizations through the use of specific algorithms, GPU implementation, or others.
+- Possibility for addon libraries to provide own implementation (so that any external libraries can be made compatible without being implemented internally by p5.js).
---
### Reference
+
The inline reference of p5.js while following a largely compatible syntax with JSDoc, under the hood is actually compiled with YUIDoc into a JSON file that the website can consume to render the actual reference page. While YUIDoc serves its purpose since the first reference documentation of p5.js, it is a tool that is no longer being worked on for over 6 years. In these 6 years, many issues and limitations means that p5.js is tied in some ways to how YUIDoc expects documentation to be authored. Several issues with setting up a development environment has also previously been tied to YUIDoc getting outdated with the overall JS ecosystem.
While YUIDoc has a very similar syntax with JSDoc, there are subtle differences. For p5.js 2.0, we will move inline reference authoring to use the JSDoc syntax and generation/compilation to use [Documentation.js](https://github.com/documentationjs/documentation).
#### Syntax
+
JSDoc syntax has for the most part settled into being a de facto standard for documentation authoring in the JavaScript ecosystem, in fact the current inline reference of p5.js is actually full compatible with JSDoc, in that it successfully compiles with JSDoc with no errors. This does not mean documentation generated through JSDoc will have the correct structure, only that it is valid syntax.
Depending on the next step and the structure of what the website might expect from the reference data file, there will likely need to be minor changes to the reference throughout the codebase.
#### Compilation
+
For compililng the inline reference into data file that can be used by the website to generate the reference pages, instead of using JSDoc, Documentation.js will be used. The reason to prefer Documentation.js over JSDoc in mainly in terms of build tool ergonomics. JSDoc is designed as a site generator with its ability to generate JSON output more of a debugging functionality instead of an intended feature; Documentation.js however supports generating JSON data files as its primary output.
Documentation.js does not have as active a maintenance history as JSDoc which is a potential downside. However, since it fully supports JSDoc syntax and JSDoc syntax having widespread ubiquity, in the future if need be the transition to another tool (even JSDoc) should not require the same effort as transitioning from YUIDoc to JSDoc since the inline reference will stay the same still.
#### Typing
+
A benefit fully adopting JSDoc syntax is that it enables direct generation of Typescript type declarations (and type checks if desired) from the JSDoc definitions. This is similar to how the source code of [SvelteKit](https://github.com/sveltejs/kit) is authored using just JavaScript while keeping type generation and type checks.
This feature however is a nice to have. p5.js will aim to follow typing rules as much as possible but will not impose it as a strict requirement for contributors. Type declaration generation will also not be brought into the repo itself (including type checks) which also means types won't be published officially.
@@ -391,47 +433,55 @@ This feature however is a nice to have. p5.js will aim to follow typing rules as
---
### Typography
+
The typography module can benefit from an overall refactor or even rewrite. The aim should be to leverage native browser/CSS capabilities as much as possible and limit the use of external library (ie. opentype.js) to only things not achievable otherwise.
---
### Misc
+
These are a collection of minor modifications that don't necessarily have enough scope to be a full proposal on its own.
-* Remove some internal aliases to Web API. Their API references can be kept.
-* Retire `p5.Table` for more semantic JavaScript data structure.
-* Turn all constants that don't rely on underlying value to use `Symbol`.
-* Remove support for JSONP in `loadJSON` function.
- * This feature adds complexity and bypasses security feature.
- * If a server was able to serve JSONP, it should be able to serve CORS resources instead.
- * If required, JSONP functionality can be readded through an addon but it should not be part of p5.js core.
-* Regression fixes
- * FES should not be using `eval()` partly because it is not compatible with Rollup's bundling
+
+- Remove some internal aliases to Web API. Their API references can be kept.
+- Retire `p5.Table` for more semantic JavaScript data structure.
+- Turn all constants that don't rely on underlying value to use `Symbol`.
+- Remove support for JSONP in `loadJSON` function.
+ - This feature adds complexity and bypasses security feature.
+ - If a server was able to serve JSONP, it should be able to serve CORS resources instead.
+ - If required, JSONP functionality can be readded through an addon but it should not be part of p5.js core.
+- Regression fixes
+ - FES should not be using `eval()` partly because it is not compatible with Rollup's bundling
---
## Future
+
The new architecture design should aim for as much pluggability as possible, enabling additional features to be changed, added, or extended through addon libraries. Some of the possible future features that may be implemented are listed below along with reasons they are and are not targeted for immediate implementation.
-* [OffscreenCanvas](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas)
- * Provides a possibility of rendering `p5.Graphics` in web worker, potentially offloading frequent drawing of many graphics into multi-threaded like environment.
- * Considered using `creatElement('canvas')` then `transferControlToOffscreen()` (and other methods) to move a `p5.Graphics` canvas to the web worker. The offscreen canvas can then be drawn onto the on screen canvas with `drawImage()`.
- * WebGL context not currently supported in Safari.
- * Firefox currently does not support drawing offscreen canvas onto on screen canvas. Alternative of `transferToImageBitmap()` is too slow to achieve real time performance.
+
+- [OffscreenCanvas](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas)
+ - Provides a possibility of rendering `p5.Graphics` in web worker, potentially offloading frequent drawing of many graphics into multi-threaded like environment.
+ - Considered using `creatElement('canvas')` then `transferControlToOffscreen()` (and other methods) to move a `p5.Graphics` canvas to the web worker. The offscreen canvas can then be drawn onto the on screen canvas with `drawImage()`.
+ - WebGL context not currently supported in Safari.
+ - Firefox currently does not support drawing offscreen canvas onto on screen canvas. Alternative of `transferToImageBitmap()` is too slow to achieve real time performance.
### p5.js 1.0
+
The 1.x version of p5.js will continue to receive bug and critical fixes for another 12 months after the release of p5.js 2.0. No new features or feature updates will be accepted to 1.x version of p5.js and new releases will be created only when as necessary. Any existing code or addon libraries should aim to migrate to support p5.js 2.0 where possible.
## Conclusion
+
All the above ideas for p5.js 2.0 would not be possible without all the people who has worked on p5.js over the years, all the contributors who provided so much insight and ideas that greatly inspired this project, the many different open source projects and their contributors that directly or indirectly enabled p5.js to be the project that it is, and many many more.
We welcome you to participate in this process of realizing p5.js 2.0 and to continue contributing to p5.js in the future as well!
## References
-* https://github.com/AndreasMadsen/xorshift
-* https://learn.jquery.com/plugins/basic-plugin-creation/
-* https://day.js.org/docs/en/plugin/plugin
-* https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
-* https://colorjs.io/docs/gamut-mapping
-* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
-* https://rollupjs.org/configuration-options/
-* https://vitejs.dev/guide/
-* https://vitest.dev/guide/browser.html
+
+- https://github.com/AndreasMadsen/xorshift
+- https://learn.jquery.com/plugins/basic-plugin-creation/
+- https://day.js.org/docs/en/plugin/plugin
+- https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
+- https://colorjs.io/docs/gamut-mapping
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
+- https://rollupjs.org/configuration-options/
+- https://vitejs.dev/guide/
+- https://vitest.dev/guide/browser.html
diff --git a/rolldown.config.js b/rolldown.config.js
new file mode 100644
index 0000000000..3bc9e19d88
--- /dev/null
+++ b/rolldown.config.js
@@ -0,0 +1,95 @@
+import { defineConfig } from 'rolldown';
+import { replacePlugin } from 'rolldown/plugins';
+import { string } from 'rollup-plugin-string';
+import pkg from './package.json' with { type: 'json' };
+import { globSync } from 'glob';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import alias from '@rollup/plugin-alias';
+// import { bundleAnalyzerPlugin } from 'rolldown/experimental';
+
+const plugins = [
+ string({
+ include: 'src/webgl/shaders/**/*'
+ }),
+ replacePlugin(
+ {
+ VERSION_WILL_BE_REPLACED_BY_BUILD: pkg.version
+ },
+ {
+ preventAssignment: true
+ }
+ )
+];
+const banner = `/*! p5.js v${pkg.version} ${new Intl.DateTimeFormat('en-US', { month: 'long', day: 'numeric', year: 'numeric' }).format(new Date())} */`;
+export default defineConfig([
+ {
+ input: 'src/app.js',
+ output: [
+ {
+ file: './lib/p5.js',
+ format: 'iife',
+ name: 'p5',
+ banner
+ },
+ {
+ file: './lib/p5.esm.js',
+ format: 'esm',
+ banner
+ },
+ {
+ file: './lib/p5.esm.min.js',
+ format: 'esm',
+ banner,
+ minify: true
+ }
+ ],
+ plugins
+ },
+ {
+ input: 'src/app.js',
+ output: {
+ file: './lib/p5.min.js',
+ format: 'iife',
+ name: 'p5',
+ banner,
+ minify: true
+ },
+ plugins: [
+ ...plugins,
+ alias({
+ entries: [
+ { find: './core/friendly_errors', replacement: './core/noop' }
+ ]
+ }),
+ replacePlugin({
+ IS_MINIFIED: true
+ })
+ // bundleAnalyzerPlugin({
+ // format: 'md'
+ // })
+ ]
+ },
+ //// ESM source build ////
+ {
+ input: Object.fromEntries(
+ globSync('src/**/*.js').map(file => [
+ // This removes `src/` as well as the file extension from each
+ // file, so e.g. src/nested/foo.js becomes nested/foo
+ path.relative(
+ 'src',
+ file.slice(0, file.length - path.extname(file).length)
+ ),
+ // This expands the relative paths to absolute paths, so e.g.
+ // src/nested/foo becomes /project/src/nested/foo.js
+ fileURLToPath(new URL(file, import.meta.url))
+ ])
+ ),
+ output: {
+ format: 'es',
+ dir: 'dist'
+ },
+ external: /node_modules\/(?!gifenc)/,
+ plugins
+ }
+]);
diff --git a/rollup.config.mjs b/rollup.config.mjs
deleted file mode 100644
index 208499561c..0000000000
--- a/rollup.config.mjs
+++ /dev/null
@@ -1,200 +0,0 @@
-import { nodeResolve } from '@rollup/plugin-node-resolve';
-import json from '@rollup/plugin-json';
-import { string } from 'rollup-plugin-string';
-import commonjs from '@rollup/plugin-commonjs';
-import terser from '@rollup/plugin-terser';
-import pkg from './package.json' with { type: 'json' };
-import { visualizer } from 'rollup-plugin-visualizer';
-import replace from '@rollup/plugin-replace';
-import alias from '@rollup/plugin-alias';
-import { globSync } from 'glob';
-import path from 'node:path';
-import { fileURLToPath } from 'node:url';
-import { rmSync } from 'node:fs';
-
-const plugins = [
- commonjs(),
- nodeResolve(),
- json(),
- string({
- include: 'src/webgl/shaders/**/*'
- }),
- replace({
- values: {
- 'VERSION_WILL_BE_REPLACED_BY_BUILD': pkg.version
- },
- preventAssignment: true
- })
-];
-const banner = `/*! p5.js v${pkg.version} ${new Intl.DateTimeFormat('en-US', { month: 'long', day: 'numeric', year: 'numeric' }).format(new Date())} */`;
-const bundleSize = (name, sourcemap) => {
- return visualizer({
- filename: `analyzer/${name}.html`,
- gzipSize: true,
- brotliSize: true,
- sourcemap
- });
-};
-
-const modules = ['webgpu']; // TODO: also generate math build
-const generateModuleBuild = () => {
- return modules.map(module => {
- return {
- input: `src/${module}/index.js`,
- output: [
- {
- file: `./lib/p5.${module}.js`,
- format: 'iife',
- plugins: [
- bundleSize(`p5.${module}.js`)
- ]
- },
- {
- file: `./lib/p5.${module}.min.js`,
- format: 'iife',
- sourcemap: 'hidden',
- plugins: [
- terser({
- compress: {
- global_defs: {
- IS_MINIFIED: true
- }
- },
- format: {
- comments: false
- }
- }),
- bundleSize(`p5.${module}.min.js`)
- ]
- },
- {
- file: `./lib/p5.${module}.esm.js`,
- format: 'esm',
- plugins: [
- bundleSize(`p5.${module}.esm.js`)
- ]
- }
- ],
- external: ['../core/main'],
- plugins: [
- ...plugins
- ]
- };
- });
-};
-
-rmSync('./dist', {
- force: true,
- recursive: true
-});
-
-export default [
- //// Library builds (IIFE and ESM) ////
- {
- input: 'src/app.js',
- output: [
- {
- file: './lib/p5.js',
- format: 'iife',
- name: 'p5',
- banner,
- plugins: [
- bundleSize('p5.js')
- ]
- },
- {
- file: './lib/p5.esm.js',
- format: 'esm',
- banner,
- plugins: [
- bundleSize('p5.esm.js')
- ]
- },
- {
- file: './lib/p5.esm.min.js',
- format: 'esm',
- banner,
- sourcemap: 'hidden',
- plugins: [
- terser({
- compress: {
- global_defs: {
- IS_MINIFIED: true
- }
- },
- format: {
- comments: false
- }
- }),
- bundleSize('p5.esm.min.js', true)
- ]
- }
- ],
- treeshake: {
- preset: 'smallest'
- },
- plugins: [
- ...plugins
- ]
- },
- //// Minified build ////
- {
- input: 'src/app.js',
- output: [
- {
- file: './lib/p5.min.js',
- format: 'iife',
- name: 'p5',
- banner,
- sourcemap: 'hidden',
- plugins: [
- terser({
- compress: {
- global_defs: {
- IS_MINIFIED: true
- }
- },
- format: {
- comments: false
- }
- }),
- bundleSize('p5.min.js', true)
- ]
- }
- ],
- treeshake: {
- preset: 'smallest'
- },
- plugins: [
- alias({
- entries: [
- { find: './core/friendly_errors', replacement: './core/noop' }
- ]
- }),
- ...plugins
- ]
- },
- //// ESM source build ////
- {
- input: Object.fromEntries(
- globSync('src/**/*.js').map(file => [
- // This removes `src/` as well as the file extension from each
- // file, so e.g. src/nested/foo.js becomes nested/foo
- path.relative(
- 'src',
- file.slice(0, file.length - path.extname(file).length)
- ),
- // This expands the relative paths to absolute paths, so e.g.
- // src/nested/foo becomes /project/src/nested/foo.js
- fileURLToPath(new URL(file, import.meta.url))
- ])
- ),
- output: {
- format: 'es',
- dir: 'dist'
- },
- external: /node_modules\/(?!gifenc)/,
- plugins
- },
- ...generateModuleBuild()
-];
diff --git a/src/README.md b/src/README.md
index e6b430609d..d9af00fd0b 100644
--- a/src/README.md
+++ b/src/README.md
@@ -4,7 +4,7 @@ The p5.js source code is organized into subdirectories which are roughly concept
The `src/core` directory is the primary exception to this. It contains most of the internal logic of p5.js — the code which orchestrates everything else. It is possible to create optimized minimalist [custom builds](/contributor_docs/custom_p5_build.md) of the p5.js library which only include specific desired modules. In such a custom build, the contents of the core directory would be the only hard requirement, and everything else would be optional.
-[JSDoc](https://jsdoc.app/) [module](https://jsdoc.app/tags-module.html) annotations are used throughout the codebase, but they primarily organize the public-facing [p5 reference manual](https://p5js.org/reference/), which is built automatically from the source code. These annotations structure the *expressive and creative API* of p5, and consequently they might not always align exactly with the *source code structure*.
+[JSDoc](https://jsdoc.app/) [module](https://jsdoc.app/tags-module.html) annotations are used throughout the codebase, but they primarily organize the public-facing [p5 reference manual](https://p5js.org/reference/), which is built automatically from the source code. These annotations structure the _expressive and creative API_ of p5, and consequently they might not always align exactly with the _source code structure_.
The `app.js` file is simply an index of all the other modules which exports the p5 constructor function.
@@ -24,4 +24,4 @@ Native browser functionality is also often aliased with properties on the `p5` o
## Internal
-Internal APIs which are not exposed to the user but are used for coordination within the library should generally take the form of constructor functions which can be exported across module boundaries. Those constructors are often attached to the `p5` constructor as a form of namespacing, but otherwise do not meaningfully act as methods that reference the host object. In cases where a `p5` object is needed inside a constructor, an instance will be explicitly passed as an input argument.
\ No newline at end of file
+Internal APIs which are not exposed to the user but are used for coordination within the library should generally take the form of constructor functions which can be exported across module boundaries. Those constructors are often attached to the `p5` constructor as a form of namespacing, but otherwise do not meaningfully act as methods that reference the host object. In cases where a `p5` object is needed inside a constructor, an instance will be explicitly passed as an input argument.
diff --git a/src/accessibility/color_namer.js b/src/accessibility/color_namer.js
index 9a9225c2eb..c4e36f6b1d 100644
--- a/src/accessibility/color_namer.js
+++ b/src/accessibility/color_namer.js
@@ -6,7 +6,7 @@
import color_conversion from '../color/color_conversion';
-function colorNamer(p5, fn){
+function colorNamer(p5, fn) {
//stores the original hsb values
let originalHSB;
@@ -702,7 +702,7 @@ function colorNamer(p5, fn){
}
//gets rgba and returs a color name
- fn._rgbColorName = function(arg) {
+ fn._rgbColorName = function (arg) {
//conversts rgba to hsb
let hsb = color_conversion._rgbaToHSBA(arg);
//stores hsb in global variable
@@ -714,6 +714,6 @@ function colorNamer(p5, fn){
export default colorNamer;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
colorNamer(p5, p5.prototype);
}
diff --git a/src/accessibility/describe.js b/src/accessibility/describe.js
index 8310fda3bf..1d5511cbdf 100644
--- a/src/accessibility/describe.js
+++ b/src/accessibility/describe.js
@@ -4,7 +4,7 @@
* @for p5
*/
-function describe(p5, fn){
+function describe(p5, fn) {
const descContainer = '_Description'; //Fallback container
const fallbackDescId = '_fallbackDesc'; //Fallback description
const fallbackTableId = '_fallbackTable'; //Fallback Table
@@ -110,7 +110,7 @@ function describe(p5, fn){
* describe(`A green circle at (${x}, 50) moves from left to right on a gray square.`, LABEL);
* }
*/
- fn.describe = function(text, display) {
+ fn.describe = function (text, display) {
// p5._validateParameters('describe', arguments);
if (typeof text !== 'string') {
return;
@@ -229,7 +229,7 @@ function describe(p5, fn){
* }
*/
- fn.describeElement = function(name, text, display) {
+ fn.describeElement = function (name, text, display) {
// p5._validateParameters('describeElement', arguments);
if (typeof text !== 'string' || typeof name !== 'string') {
return;
@@ -313,7 +313,7 @@ function describe(p5, fn){
*/
//creates HTML structure for canvas descriptions
- fn._describeHTML = function(type, text) {
+ fn._describeHTML = function (type, text) {
const cnvId = this.canvas.id;
if (type === 'fallback') {
//if there is no description container
@@ -401,7 +401,7 @@ function describe(p5, fn){
}
//creates HTML structure for element descriptions
- fn._describeElementHTML = function(type, name, text) {
+ fn._describeElementHTML = function (type, name, text) {
const cnvId = this.canvas.id;
if (type === 'fallback') {
//if there is no description container
@@ -487,6 +487,6 @@ function describe(p5, fn){
export default describe;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
describe(p5, p5.prototype);
}
diff --git a/src/accessibility/gridOutput.js b/src/accessibility/gridOutput.js
index fc3e67ed78..512d7e05ee 100644
--- a/src/accessibility/gridOutput.js
+++ b/src/accessibility/gridOutput.js
@@ -4,11 +4,11 @@
* @for p5
*/
-function gridOutput(p5, fn){
+function gridOutput(p5, fn) {
//the functions in this file support updating the grid output
//updates gridOutput
- fn._updateGridOutput = function(idT) {
+ fn._updateGridOutput = function (idT) {
if (this._renderer && this._renderer.isP3D) {
if (!this._didOutputGridWebGLMessage) {
this._didOutputGridWebGLMessage = true;
@@ -70,10 +70,10 @@ function gridOutput(p5, fn){
}
// Check if shape is in canvas, skip if not
- if(
+ if (
ingredients[x][y].loc.locY < cells.length &&
ingredients[x][y].loc.locX < cells[ingredients[x][y].loc.locY].length
- ){
+ ) {
//if empty cell of location of shape is undefined
if (!cells[ingredients[x][y].loc.locY][ingredients[x][y].loc.locX]) {
//fill it with shape info
@@ -161,6 +161,6 @@ function gridOutput(p5, fn){
export default gridOutput;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
gridOutput(p5, p5.prototype);
}
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 4469b4fdef..e9d659e596 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -4,7 +4,7 @@ import textOutput from './textOutput.js';
import outputs from './outputs.js';
import colorNamer from './color_namer.js';
-export default function(p5){
+export default function (p5) {
p5.registerAddon(describe);
p5.registerAddon(gridOutput);
p5.registerAddon(textOutput);
diff --git a/src/accessibility/outputs.js b/src/accessibility/outputs.js
index 5fd5183016..cffcd19165 100644
--- a/src/accessibility/outputs.js
+++ b/src/accessibility/outputs.js
@@ -4,7 +4,7 @@
* @for p5
*/
-function outputs(p5, fn){
+function outputs(p5, fn) {
/**
* Creates a screen reader-accessible description of shapes on the canvas.
*
@@ -114,7 +114,7 @@ function outputs(p5, fn){
* describe('A red circle moves from left to right above a blue square.');
* }
*/
- fn.textOutput = function(display) {
+ fn.textOutput = function (display) {
// p5._validateParameters('textOutput', arguments);
//if textOutput is already true
if (this._accessibleOutputs.text) {
@@ -243,7 +243,7 @@ function outputs(p5, fn){
* describe('A red circle moves from left to right above a blue square.');
* }
*/
- fn.gridOutput = function(display) {
+ fn.gridOutput = function (display) {
// p5._validateParameters('gridOutput', arguments);
//if gridOutput is already true
if (this._accessibleOutputs.grid) {
@@ -263,7 +263,7 @@ function outputs(p5, fn){
};
//helper function returns true when accessible outputs are true
- fn._addAccsOutput = function() {
+ fn._addAccsOutput = function () {
//if there are no accessible outputs create object with all false
if (!this._accessibleOutputs) {
this._accessibleOutputs = {
@@ -277,7 +277,7 @@ function outputs(p5, fn){
};
//helper function that creates html structure for accessible outputs
- fn._createOutput = function(type, display) {
+ fn._createOutput = function (type, display) {
let cnvId = this.canvas.id;
//if there are no ingredients create object. this object stores data for the outputs
if (!this.ingredients) {
@@ -301,9 +301,8 @@ function outputs(p5, fn){
//if there is no canvas description (see describe() and describeElement())
if (!this.dummyDOM.querySelector(`#${cnvId}_Description`)) {
//create html structure inside of canvas
- this.dummyDOM.querySelector(
- `#${cnvId}`
- ).innerHTML = ``;
+ this.dummyDOM.querySelector(`#${cnvId}`).innerHTML =
+ ``;
} else {
//create html structure after canvas description container
this.dummyDOM
@@ -358,7 +357,9 @@ function outputs(p5, fn){
//if textOutput already exists
if (this.dummyDOM.querySelector(query)) {
//create gridOutput after textOutput
- this.dummyDOM.querySelector(query).insertAdjacentHTML('afterend', inner);
+ this.dummyDOM
+ .querySelector(query)
+ .insertAdjacentHTML('afterend', inner);
} else {
//create output inside of container
this.dummyDOM.querySelector(`#${container}`).innerHTML = inner;
@@ -378,7 +379,7 @@ function outputs(p5, fn){
//this function is called at the end of setup and draw if using
//accessibleOutputs and calls update functions of outputs
- fn._updateAccsOutput = function() {
+ fn._updateAccsOutput = function () {
let cnvId = this.canvas.id;
//if the shapes are not the same as before
if (
@@ -404,7 +405,7 @@ function outputs(p5, fn){
//helper function that resets all ingredients when background is called
//and saves background color name
- fn._accsBackground = function(args) {
+ fn._accsBackground = function (args) {
//save current shapes as string in pShapes
this.ingredients.pShapes = JSON.stringify(this.ingredients.shapes);
this.ingredients.pBackground = this.ingredients.colors.background;
@@ -418,7 +419,7 @@ function outputs(p5, fn){
};
//helper function that gets fill and stroke of shapes
- fn._accsCanvasColors = function(f, args) {
+ fn._accsCanvasColors = function (f, args) {
if (f === 'fill') {
//update fill different
if (this.ingredients.colors.fillRGBA !== args) {
@@ -435,7 +436,7 @@ function outputs(p5, fn){
};
//builds ingredients.shapes used for building outputs
- fn._accsOutput = function(f, args) {
+ fn._accsOutput = function (f, args) {
if (f === 'ellipse' && args[2] === args[3]) {
f = 'circle';
} else if (f === 'rectangle' && args[2] === args[3]) {
@@ -480,7 +481,7 @@ function outputs(p5, fn){
if (!this.ingredients.shapes[f]) {
this.ingredients.shapes[f] = [include];
//if other shapes of this type have been created
- } else{
+ } else {
//for every shape of this type
for (let y in this.ingredients.shapes[f]) {
//compare it with current shape and if it already exists make add false
@@ -528,8 +529,9 @@ function outputs(p5, fn){
//gets position of shape in the canvas
fn._getPos = function (x, y) {
- const { x: transformedX, y: transformedY } =
- this.worldToScreen(new p5.Vector(x, y));
+ const { x: transformedX, y: transformedY } = this.worldToScreen(
+ new p5.Vector(x, y)
+ );
const canvasWidth = this.width;
const canvasHeight = this.height;
if (transformedX < 0.4 * canvasWidth) {
@@ -563,8 +565,8 @@ function outputs(p5, fn){
function _canvasLocator(args, canvasWidth, canvasHeight) {
const noRows = 10;
const noCols = 10;
- let locX = Math.floor(args[0] / canvasWidth * noRows);
- let locY = Math.floor(args[1] / canvasHeight * noCols);
+ let locX = Math.floor((args[0] / canvasWidth) * noRows);
+ let locY = Math.floor((args[1] / canvasHeight) * noCols);
if (locX === noRows) {
locX = locX - 1;
}
@@ -585,22 +587,22 @@ function outputs(p5, fn){
// therefore, area of arc = difference bet. arc's start and end radians * horizontal radius * vertical radius.
// the below expression is adjusted for negative values and differences in arc's start and end radians over PI*2
const arcSizeInRadians =
- ((shapeArgs[5] - shapeArgs[4]) % (Math.PI * 2) + Math.PI * 2) %
+ (((shapeArgs[5] - shapeArgs[4]) % (Math.PI * 2)) + Math.PI * 2) %
(Math.PI * 2);
- objectArea = arcSizeInRadians * shapeArgs[2] * shapeArgs[3] / 8;
+ objectArea = (arcSizeInRadians * shapeArgs[2] * shapeArgs[3]) / 8;
if (shapeArgs[6] === 'open' || shapeArgs[6] === 'chord') {
// when the arc's mode is OPEN or CHORD, we need to account for the area of the triangle that is formed to close the arc
// (Ax( By − Cy) + Bx(Cy − Ay) + Cx(Ay − By ) )/2
const Ax = shapeArgs[0];
const Ay = shapeArgs[1];
const Bx =
- shapeArgs[0] + shapeArgs[2] / 2 * Math.cos(shapeArgs[4]).toFixed(2);
+ shapeArgs[0] + (shapeArgs[2] / 2) * Math.cos(shapeArgs[4]).toFixed(2);
const By =
- shapeArgs[1] + shapeArgs[3] / 2 * Math.sin(shapeArgs[4]).toFixed(2);
+ shapeArgs[1] + (shapeArgs[3] / 2) * Math.sin(shapeArgs[4]).toFixed(2);
const Cx =
- shapeArgs[0] + shapeArgs[2] / 2 * Math.cos(shapeArgs[5]).toFixed(2);
+ shapeArgs[0] + (shapeArgs[2] / 2) * Math.cos(shapeArgs[5]).toFixed(2);
const Cy =
- shapeArgs[1] + shapeArgs[3] / 2 * Math.sin(shapeArgs[5]).toFixed(2);
+ shapeArgs[1] + (shapeArgs[3] / 2) * Math.sin(shapeArgs[5]).toFixed(2);
const areaOfExtraTriangle =
Math.abs(Ax * (By - Cy) + Bx * (Cy - Ay) + Cx * (Ay - By)) / 2;
if (arcSizeInRadians > Math.PI) {
@@ -610,7 +612,7 @@ function outputs(p5, fn){
}
}
} else if (objectType === 'ellipse' || objectType === 'circle') {
- objectArea = 3.14 * shapeArgs[2] / 2 * shapeArgs[3] / 2;
+ objectArea = (((3.14 * shapeArgs[2]) / 2) * shapeArgs[3]) / 2;
} else if (objectType === 'line') {
objectArea = 0;
} else if (objectType === 'point') {
@@ -645,26 +647,27 @@ function outputs(p5, fn){
new DOMPoint(0, canvasHeight)
];
// Apply the inverse of the current transformations to the canvas corners
- const currentTransform = this._renderer.isP3D ?
- new DOMMatrix(this._renderer.uMVMatrix.mat4) :
- this.drawingContext.getTransform();
+ const currentTransform = this._renderer.isP3D
+ ? new DOMMatrix(this._renderer.uMVMatrix.mat4)
+ : this.drawingContext.getTransform();
const invertedTransform = currentTransform.inverse();
- const tc = canvasCorners.map(
- corner => corner.matrixTransform(invertedTransform)
+ const tc = canvasCorners.map(corner =>
+ corner.matrixTransform(invertedTransform)
);
/* Use same shoelace formula used for quad area (above) to calculate
the area of the canvas with inverted transformation applied */
- const transformedCanvasArea = Math.abs(
- (tc[3].x + tc[0].x) * (tc[3].y - tc[0].y) +
- (tc[0].x + tc[1].x) * (tc[0].y - tc[1].y) +
- (tc[1].x + tc[2].x) * (tc[1].y - tc[2].y)+
- (tc[2].x + tc[3].x) * (tc[2].y - tc[3].y)
- ) / 2;
+ const transformedCanvasArea =
+ Math.abs(
+ (tc[3].x + tc[0].x) * (tc[3].y - tc[0].y) +
+ (tc[0].x + tc[1].x) * (tc[0].y - tc[1].y) +
+ (tc[1].x + tc[2].x) * (tc[1].y - tc[2].y) +
+ (tc[2].x + tc[3].x) * (tc[2].y - tc[3].y)
+ ) / 2;
/* Compare area of shape (minus transformations) to area of canvas
with inverted transformation applied.
Return percentage */
const untransformedArea = Math.round(
- objectArea * 100 / (transformedCanvasArea)
+ (objectArea * 100) / transformedCanvasArea
);
return untransformedArea;
};
@@ -672,6 +675,6 @@ function outputs(p5, fn){
export default outputs;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
outputs(p5, p5.prototype);
}
diff --git a/src/accessibility/textOutput.js b/src/accessibility/textOutput.js
index 46baa56071..41d21234c4 100644
--- a/src/accessibility/textOutput.js
+++ b/src/accessibility/textOutput.js
@@ -4,11 +4,11 @@
* @for p5
*/
-function textOutput(p5, fn){
+function textOutput(p5, fn) {
//the functions in this file support updating the text output
//updates textOutput
- fn._updateTextOutput = function(idT) {
+ fn._updateTextOutput = function (idT) {
if (this._renderer && this._renderer.isP3D) {
if (!this._didOutputTextWebGLMessage) {
this._didOutputTextWebGLMessage = true;
@@ -113,7 +113,8 @@ function textOutput(p5, fn){
} else {
_line = _line + `, at ${ingredients[x][y].pos}`;
if (x !== 'point') {
- _line = _line + `, covering ${ingredients[x][y].area}% of the canvas`;
+ _line =
+ _line + `, covering ${ingredients[x][y].area}% of the canvas`;
}
_line = _line + '.';
}
@@ -127,6 +128,6 @@ function textOutput(p5, fn){
export default textOutput;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
textOutput(p5, p5.prototype);
}
diff --git a/src/app.node.js b/src/app.node.js
index ef270459b7..462c6167e8 100644
--- a/src/app.node.js
+++ b/src/app.node.js
@@ -57,4 +57,3 @@ import strands from './strands/p5.strands';
p5.registerAddon(strands);
export default p5;
-
diff --git a/src/color/color_conversion.js b/src/color/color_conversion.js
index 11823f077e..20b04e218c 100644
--- a/src/color/color_conversion.js
+++ b/src/color/color_conversion.js
@@ -23,7 +23,7 @@ p5.ColorConversion = {
const val = hsba[2];
// Calculate lightness.
- const li = (2 - sat) * val / 2;
+ const li = ((2 - sat) * val) / 2;
// Convert saturation.
if (li !== 0) {
@@ -32,7 +32,7 @@ p5.ColorConversion = {
} else if (li < 0.5) {
sat = sat / (2 - sat);
} else {
- sat = sat * val / (2 - li * 2);
+ sat = (sat * val) / (2 - li * 2);
}
}
@@ -112,7 +112,7 @@ p5.ColorConversion = {
}
// Convert saturation.
- sat = 2 * (val - li) / val;
+ sat = (2 * (val - li)) / val;
// Hue and alpha stay the same.
return [hue, sat, val, hsla[3]];
diff --git a/src/color/color_spaces/hsb.js b/src/color/color_spaces/hsb.js
index 2b3ece6619..fc10fdb3ca 100644
--- a/src/color/color_spaces/hsb.js
+++ b/src/color/color_spaces/hsb.js
@@ -52,7 +52,7 @@ export default new ColorSpace({
}
}
- return [hue / 6 * 360, sat * 100, val * 100];
+ return [(hue / 6) * 360, sat * 100, val * 100];
},
toBase,
@@ -61,13 +61,9 @@ export default new ColorSpace({
type: 'custom',
serialize: (coords, alpha) => {
const rgb = toBase(coords);
- let ret = `rgb(${
- Math.round(rgb[0] * 100 * 100) / 100
- }% ${
+ let ret = `rgb(${Math.round(rgb[0] * 100 * 100) / 100}% ${
Math.round(rgb[1] * 100 * 100) / 100
- }% ${
- Math.round(rgb[2] * 100 * 100) / 100
- }%`;
+ }% ${Math.round(rgb[2] * 100 * 100) / 100}%`;
if (alpha < 1) {
ret += ` / ${alpha}`;
@@ -78,10 +74,10 @@ export default new ColorSpace({
return ret;
}
},
- 'hsb': {
+ hsb: {
coords: [' | ', '', '']
},
- 'hsba': {
+ hsba: {
coords: [' | ', '', ''],
commans: true,
lastAlpha: true
@@ -89,8 +85,8 @@ export default new ColorSpace({
}
});
-function toBase(hsb){
- const hue = hsb[0] / 360 * 6; // We will split hue into 6 sectors.
+function toBase(hsb) {
+ const hue = (hsb[0] / 360) * 6; // We will split hue into 6 sectors.
const sat = hsb[1] / 100;
const val = hsb[2] / 100;
diff --git a/src/color/creating_reading.js b/src/color/creating_reading.js
index 39f16db09d..2cf40ce596 100644
--- a/src/color/creating_reading.js
+++ b/src/color/creating_reading.js
@@ -71,7 +71,7 @@ export const OKLCH = 'oklch';
*/
export const RGBA = 'rgba';
-function creatingReading(p5, fn){
+function creatingReading(p5, fn) {
fn.RGB = RGB;
fn.RGBP3 = RGBP3;
fn.HSB = HSB;
@@ -100,7 +100,7 @@ function creatingReading(p5, fn){
[OKLAB]: [100, [-125, 125], [-125, 125], 1],
[OKLCH]: [100, 150, 360, 1],
- clone: function(){
+ clone: function () {
const cloned = { ...this };
for (const key in cloned) {
if (cloned[key] instanceof Array) {
@@ -403,7 +403,7 @@ function creatingReading(p5, fn){
* @param {p5.Color} color
* @return {p5.Color}
*/
- fn.color = function(...args) {
+ fn.color = function (...args) {
// p5._validateParameters('color', args);
if (args[0] instanceof Color) {
// TODO: perhaps change color mode to match instance mode?
@@ -566,7 +566,7 @@ function creatingReading(p5, fn){
* color components, or CSS color string.
* @return {Number} the red value.
*/
- fn.red = function(c) {
+ fn.red = function (c) {
// p5._validateParameters('red', arguments);
// Get current red max
return this.color(c)._getRed();
@@ -719,7 +719,7 @@ function creatingReading(p5, fn){
* color components, or CSS color string.
* @return {Number} the green value.
*/
- fn.green = function(c) {
+ fn.green = function (c) {
// p5._validateParameters('green', arguments);
// Get current green max
return this.color(c)._getGreen();
@@ -872,7 +872,7 @@ function creatingReading(p5, fn){
* color components, or CSS color string.
* @return {Number} the blue value.
*/
- fn.blue = function(c) {
+ fn.blue = function (c) {
// p5._validateParameters('blue', arguments);
// Get current blue max
return this.color(c)._getBlue();
@@ -993,7 +993,7 @@ function creatingReading(p5, fn){
* color components, or CSS color string.
* @return {Number} the alpha value.
*/
- fn.alpha = function(c) {
+ fn.alpha = function (c) {
// p5._validateParameters('alpha', arguments);
// Get current alpha max
return this.color(c)._getAlpha();
@@ -1134,19 +1134,19 @@ function creatingReading(p5, fn){
* color components, or CSS color string.
* @return {Number} the hue value.
*/
- fn.hue = function(c) {
+ fn.hue = function (c) {
let colorMode = HSL;
let i = 0;
- if(
+ if (
this._renderer.states.colorMode === HSB ||
this._renderer.states.colorMode === HSL
- ){
+ ) {
colorMode = this._renderer.states.colorMode;
- }else if(
+ } else if (
this._renderer.states.colorMode === LCH ||
this._renderer.states.colorMode === OKLCH
- ){
+ ) {
colorMode = this._renderer.states.colorMode;
i = 2;
}
@@ -1341,8 +1341,8 @@ function creatingReading(p5, fn){
* color components, or CSS color string.
* @return {Number} the saturation value
*/
- fn.saturation = function(c) {
- const colorMode = (this._renderer.states.colorMode === HSB) ? HSB : HSL;
+ fn.saturation = function (c) {
+ const colorMode = this._renderer.states.colorMode === HSB ? HSB : HSL;
return this.color(c)._getSaturation(
this._renderer.states.colorMaxes[colorMode][1]
);
@@ -1504,7 +1504,7 @@ function creatingReading(p5, fn){
* color components, or CSS color string.
* @return {Number} the brightness value.
*/
- fn.brightness = function(c) {
+ fn.brightness = function (c) {
return this.color(c)._getBrightness(
this._renderer.states.colorMaxes.hsb[2]
);
@@ -1666,10 +1666,8 @@ function creatingReading(p5, fn){
* color components, or CSS color string.
* @return {Number} the lightness value.
*/
- fn.lightness = function(c) {
- return this.color(c)._getLightness(
- this._renderer.states.colorMaxes.hsl[2]
- );
+ fn.lightness = function (c) {
+ return this.color(c)._getLightness(this._renderer.states.colorMaxes.hsl[2]);
};
/**
@@ -1757,7 +1755,7 @@ function creatingReading(p5, fn){
* @param {Number} amt number between 0 and 1.
* @return {p5.Color} interpolated color.
*/
- fn.lerpColor = function(c1, c2, amt) {
+ fn.lerpColor = function (c1, c2, amt) {
// p5._validateParameters('lerpColor', arguments);
return c1.lerp(c2, amt, this._renderer.states.colorMode);
};
@@ -1793,10 +1791,9 @@ function creatingReading(p5, fn){
* ], millis() / 10000 % 1));
* }
*/
- fn.paletteLerp = function(color_stops, amt) {
+ fn.paletteLerp = function (color_stops, amt) {
const first_color_stop = color_stops[0];
- if (amt < first_color_stop[1])
- return this.color(first_color_stop[0]);
+ if (amt < first_color_stop[1]) return this.color(first_color_stop[0]);
for (let i = 1; i < color_stops.length; i++) {
const color_stop = color_stops[i];
@@ -1816,6 +1813,6 @@ function creatingReading(p5, fn){
export default creatingReading;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
creatingReading(p5, p5.prototype);
-}
\ No newline at end of file
+}
diff --git a/src/color/index.js b/src/color/index.js
index bd0fb96f58..935d0bc7c1 100644
--- a/src/color/index.js
+++ b/src/color/index.js
@@ -2,7 +2,7 @@ import creatingReading from './creating_reading.js';
import p5color from './p5.Color.js';
import setting from './setting.js';
-export default function(p5){
+export default function (p5) {
p5.registerAddon(creatingReading);
p5.registerAddon(p5color);
p5.registerAddon(setting);
diff --git a/src/color/p5.Color.culori.js b/src/color/p5.Color.culori.js
index 12ce7e724d..dbbb42bd66 100644
--- a/src/color/p5.Color.culori.js
+++ b/src/color/p5.Color.culori.js
@@ -4,63 +4,33 @@ export class Color {
mode;
array;
- static addColorMode(mode, definition){
+ static addColorMode(mode, definition) {}
- }
+ constructor(vals) {}
- constructor(vals){
+ toString() {}
- }
+ setRed(new_red) {}
- toString(){
+ setGreen(new_green) {}
- }
+ setBlue(new_blue) {}
- setRed(new_red){
+ setAlpha(new_alpha) {}
- }
+ _getRed() {}
- setGreen(new_green){
+ _getGreen() {}
- }
+ _getBlue() {}
- setBlue(new_blue){
+ _getAlpha() {}
- }
+ _getHue() {}
- setAlpha(new_alpha){
+ _getSaturation() {}
- }
+ _getBrightness() {}
- _getRed(){
-
- }
-
- _getGreen(){
-
- }
-
- _getBlue(){
-
- }
-
- _getAlpha(){
-
- }
-
- _getHue(){
-
- }
-
- _getSaturation(){
-
- }
-
- _getBrightness(){
-
- }
-
- _getLightness(){
-
- }
+ _getLightness() {}
}
diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js
index fb4b62a4f8..ea852ac80a 100644
--- a/src/color/p5.Color.js
+++ b/src/color/p5.Color.js
@@ -4,8 +4,17 @@
* @for p5
*/
-import { RGB, RGBP3, HSL, HSB, HWB, LAB, LCH, OKLAB, OKLCH } from './creating_reading';
-
+import {
+ RGB,
+ RGBP3,
+ HSL,
+ HSB,
+ HWB,
+ LAB,
+ LCH,
+ OKLAB,
+ OKLCH
+} from './creating_reading';
import {
ColorSpace,
@@ -14,14 +23,11 @@ import {
serialize,
parse,
range,
-
sRGB,
HSL as HSLSpace,
HWB as HWBSpace,
-
Lab,
LCH as LCHSpace,
-
OKLab,
OKLCH as OKLCHSpace,
contrastWCAG21,
@@ -31,29 +37,26 @@ import {
import HSBSpace from './color_spaces/hsb.js';
const map = (n, start1, stop1, start2, stop2, clamp) => {
- let result = ((n - start1) / (stop1 - start1) * (stop2 - start2) + start2);
+ let result = ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
if (clamp) {
result = Math.max(result, Math.min(start2, stop2));
result = Math.min(result, Math.max(start2, stop2));
}
return result;
-}
+};
-const toHexComponent = (v) => {
+const toHexComponent = v => {
const vInt = ~~(v * 255);
- const hex = vInt.toString(16)
+ const hex = vInt.toString(16);
if (hex.length < 2) {
- return '0' + hex
+ return '0' + hex;
} else {
- return hex
+ return hex;
}
-}
+};
const serializationMap = new Map();
-
-
-
class Color {
static colorMap = {};
static #colorjsMaxes = {};
@@ -68,16 +71,18 @@ class Color {
// Used to add additional color modes to p5.js
// Uses underlying library's definition
- static addColorMode(mode, definition){
+ static addColorMode(mode, definition) {
ColorSpace.register(definition);
Color.colorMap[mode] = definition.id;
// Get colorjs maxes
- Color.#colorjsMaxes[mode] = Object.values(definition.coords)
- .reduce((acc, v) => {
+ Color.#colorjsMaxes[mode] = Object.values(definition.coords).reduce(
+ (acc, v) => {
acc.push(v.refRange || v.range);
return acc;
- }, []);
+ },
+ []
+ );
Color.#colorjsMaxes[mode].push([0, 1]);
// Get grayscale mapping
@@ -88,55 +93,60 @@ class Color {
// This changes with the color object
this._cachedMode = colorMode || RGB;
- if(vals instanceof Color){
+ if (vals instanceof Color) {
// Received Color object to be used for color mode conversion
- const mode = colorMode ?
- Color.colorMap[colorMode] :
- Color.colorMap[vals.mode];
+ const mode = colorMode
+ ? Color.colorMap[colorMode]
+ : Color.colorMap[vals.mode];
this._initialize = () => {
this._cachedColor = to(vals._color, mode);
this._cachedMode = mode;
};
-
- }else if (typeof vals === 'object' && !Array.isArray(vals) && vals !== null){
+ } else if (
+ typeof vals === 'object' &&
+ !Array.isArray(vals) &&
+ vals !== null
+ ) {
// Received color.js object to be used internally
- const mode = colorMode ?
- Color.colorMap[colorMode] :
- vals.spaceId;
+ const mode = colorMode ? Color.colorMap[colorMode] : vals.spaceId;
this._initialize = () => {
this._cachedColor = to(vals, mode);
- this._cachedMode = colorMode || Object.entries(Color.colorMap)
- .find(([key, val]) => {
+ this._cachedMode =
+ colorMode ||
+ Object.entries(Color.colorMap).find(([key, val]) => {
return val === this._cachedColor.spaceId;
});
};
-
- } else if(typeof vals[0] === 'string') {
+ } else if (typeof vals[0] === 'string') {
// Received string
this._defaultStringValue = vals[0];
this._initialize = () => {
- try{
+ try {
this._cachedColor = parse(vals[0]);
const [mode] = Object.entries(Color.colorMap).find(([key, val]) => {
return val === this._cachedColor.spaceId;
});
this._cachedMode = mode;
this._cachedColor = to(this._cachedColor, this._cachedColor.spaceId);
- }catch(err){
+ } catch (err) {
// TODO: Invalid color string
throw new Error('Invalid color string');
}
};
-
- }else{
+ } else {
// Received individual channel values
let mappedVals;
- if(colorMaxes){
+ if (colorMaxes) {
// NOTE: need to consider different number of arguments (eg. CMYK)
- if(vals.length === 4){
- mappedVals = Color.mapColorRange(vals, this._cachedMode, colorMaxes, clamp);
- }else if(vals.length === 3){
+ if (vals.length === 4) {
+ mappedVals = Color.mapColorRange(
+ vals,
+ this._cachedMode,
+ colorMaxes,
+ clamp
+ );
+ } else if (vals.length === 3) {
mappedVals = Color.mapColorRange(
[vals[0], vals[1], vals[2]],
this._cachedMode,
@@ -144,15 +154,15 @@ class Color {
clamp
);
mappedVals.push(1);
- }else if(vals.length === 2){
+ } else if (vals.length === 2) {
// Grayscale with alpha
- if(Color.#grayscaleMap[this._cachedMode]){
+ if (Color.#grayscaleMap[this._cachedMode]) {
mappedVals = Color.#grayscaleMap[this._cachedMode](
vals[0],
colorMaxes,
clamp
);
- }else{
+ } else {
mappedVals = Color.mapColorRange(
[vals[0], vals[0], vals[0]],
this._cachedMode,
@@ -160,28 +170,21 @@ class Color {
clamp
);
}
- const alphaMaxes = Array.isArray(colorMaxes[colorMaxes.length-1]) ?
- colorMaxes[colorMaxes.length-1] :
- [0, colorMaxes[colorMaxes.length-1]];
+ const alphaMaxes = Array.isArray(colorMaxes[colorMaxes.length - 1])
+ ? colorMaxes[colorMaxes.length - 1]
+ : [0, colorMaxes[colorMaxes.length - 1]];
mappedVals.push(
- map(
- vals[1],
- alphaMaxes[0],
- alphaMaxes[1],
- 0,
- 1,
- clamp
- )
+ map(vals[1], alphaMaxes[0], alphaMaxes[1], 0, 1, clamp)
);
- }else if(vals.length === 1){
+ } else if (vals.length === 1) {
// Grayscale only
- if(Color.#grayscaleMap[this._cachedMode]){
+ if (Color.#grayscaleMap[this._cachedMode]) {
mappedVals = Color.#grayscaleMap[this._cachedMode](
vals[0],
colorMaxes,
clamp
);
- }else{
+ } else {
mappedVals = Color.mapColorRange(
[vals[0], vals[0], vals[0]],
this._cachedMode,
@@ -190,23 +193,34 @@ class Color {
);
}
mappedVals.push(1);
- }else{
+ } else {
throw new Error('Invalid color');
}
- }else{
+ } else {
mappedVals = vals;
}
if (this._cachedMode === RGB) {
if (mappedVals[3] === 1) {
// Faster for the browser to parse than rgba
- this._defaultStringValue = '#' + toHexComponent(mappedVals[0]) + toHexComponent(mappedVals[1]) + toHexComponent(mappedVals[2]);
+ this._defaultStringValue =
+ '#' +
+ toHexComponent(mappedVals[0]) +
+ toHexComponent(mappedVals[1]) +
+ toHexComponent(mappedVals[2]);
} else {
- this._defaultStringValue = '#' + toHexComponent(mappedVals[0]) + toHexComponent(mappedVals[1]) + toHexComponent(mappedVals[2]) + toHexComponent(mappedVals[3]);;
+ this._defaultStringValue =
+ '#' +
+ toHexComponent(mappedVals[0]) +
+ toHexComponent(mappedVals[1]) +
+ toHexComponent(mappedVals[2]) +
+ toHexComponent(mappedVals[3]);
}
}
this._initialize = () => {
- const space = Color.colorMap[this._cachedMode] || console.error('Invalid color mode');
+ const space =
+ Color.colorMap[this._cachedMode] ||
+ console.error('Invalid color mode');
const coords = mappedVals.slice(0, 3);
const color = {
@@ -245,11 +259,11 @@ class Color {
}
// Convert from p5 color range to color.js color range
- static mapColorRange(origin, mode, maxes, clamp){
+ static mapColorRange(origin, mode, maxes, clamp) {
const p5Maxes = maxes.map(max => {
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
return [0, max];
- }else{
+ } else {
return max;
}
});
@@ -258,8 +272,10 @@ class Color {
return origin.map((channel, i) => {
const newval = map(
channel,
- p5Maxes[i][0], p5Maxes[i][1],
- colorjsMaxes[i][0], colorjsMaxes[i][1],
+ p5Maxes[i][0],
+ p5Maxes[i][1],
+ colorjsMaxes[i][0],
+ colorjsMaxes[i][1],
clamp
);
return newval;
@@ -267,11 +283,11 @@ class Color {
}
// Convert from color.js color range to p5 color range
- static unmapColorRange(origin, mode, maxes){
+ static unmapColorRange(origin, mode, maxes) {
const p5Maxes = maxes.map(max => {
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
return [0, max];
- }else{
+ } else {
return max;
}
});
@@ -280,15 +296,17 @@ class Color {
return origin.map((channel, i) => {
const newval = map(
channel,
- colorjsMaxes[i][0], colorjsMaxes[i][1],
- p5Maxes[i][0], p5Maxes[i][1]
+ colorjsMaxes[i][0],
+ colorjsMaxes[i][1],
+ p5Maxes[i][0],
+ p5Maxes[i][1]
);
return newval;
});
}
// Will do conversion in-Gamut as out of Gamut conversion is only really useful for futher conversions
- #toColorMode(mode){
+ #toColorMode(mode) {
return new Color(this._color, mode);
}
@@ -297,21 +315,19 @@ class Color {
return this._getRGBA();
}
- array(){
+ array() {
return this._array;
}
- lerp(color, amt, mode){
+ lerp(color, amt, mode) {
// Find the closest common ancestor color space
let spaceIndex = -1;
- while(
- (
- spaceIndex+1 < this._color.space.path.length ||
- spaceIndex+1 < color._color.space.path.length
- ) &&
- this._color.space.path[spaceIndex+1] ===
- color._color.space.path[spaceIndex+1]
- ){
+ while (
+ (spaceIndex + 1 < this._color.space.path.length ||
+ spaceIndex + 1 < color._color.space.path.length) &&
+ this._color.space.path[spaceIndex + 1] ===
+ color._color.space.path[spaceIndex + 1]
+ ) {
spaceIndex += 1;
}
@@ -365,10 +381,10 @@ class Color {
* describe('The text "#9932cc" written in purple on a gray background.');
* }
*/
- toString(format) {
- if (format === undefined && this._defaultStringValue !== undefined) {
+ toString(format) {
+ if (format === undefined && this._defaultStringValue !== undefined) {
return this._defaultStringValue;
- }
+ }
let outputFormat = format;
if (format === '#rrggbb') {
@@ -386,19 +402,19 @@ class Color {
if (format === '#rrggbb') {
colorString = String(colorString);
if (colorString.length === 4) {
- const r = colorString[1];
- const g = colorString[2];
- const b = colorString[3];
- colorString = `#${r}${r}${g}${g}${b}${b}`;
+ const r = colorString[1];
+ const g = colorString[2];
+ const b = colorString[3];
+ colorString = `#${r}${r}${g}${g}${b}${b}`;
}
if (colorString.length > 7) {
- colorString = colorString.slice(0, 7);
+ colorString = colorString.slice(0, 7);
}
colorString = colorString.toLowerCase();
}
if (serializationMap.size > 1000) {
- serializationMap.delete(serializationMap.keys().next().value)
+ serializationMap.delete(serializationMap.keys().next().value);
}
serializationMap.set(key, colorString);
}
@@ -482,10 +498,10 @@ class Color {
* text(nf(contrast.APCA.value, 0, 2), 10, 85);
* }
*/
- contrast(other_color, options='WCAG21') {
- if(options !== 'all'){
+ contrast(other_color, options = 'WCAG21') {
+ if (options !== 'all') {
let contrastVal, minimum;
- switch(options){
+ switch (options) {
case 'WCAG21':
contrastVal = contrastWCAG21(this._color, other_color._color);
minimum = 4.5;
@@ -499,7 +515,7 @@ class Color {
}
return contrastVal >= minimum;
- }else{
+ } else {
const wcag21Value = contrastWCAG21(this._color, other_color._color);
const apcaValue = contrastAPCA(this._color, other_color._color);
return {
@@ -514,7 +530,7 @@ class Color {
}
};
}
- };
+ }
/**
* Sets the red component of a color.
@@ -548,18 +564,18 @@ class Color {
* describe('Two rectangles. The left one is salmon pink and the right one is teal.');
* }
*/
- setRed(new_red, max=[0, 1]) {
+ setRed(new_red, max = [0, 1]) {
this._defaultStringValue = undefined;
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
max = [0, max];
}
const colorjsMax = Color.#colorjsMaxes[RGB][0];
const newval = map(new_red, max[0], max[1], colorjsMax[0], colorjsMax[1]);
- if(this.mode === RGB || this.mode === RGBP3){
+ if (this.mode === RGB || this.mode === RGBP3) {
this._color.coords[0] = newval;
- }else{
+ } else {
// Will do an imprecise conversion to 'srgb', not recommended
const space = this._color.space.id;
const representation = to(this._color, 'srgb');
@@ -600,18 +616,18 @@ class Color {
* describe('Two rectangles. The left one is salmon pink and the right one is yellow.');
* }
*/
- setGreen(new_green, max=[0, 1]) {
+ setGreen(new_green, max = [0, 1]) {
this._defaultStringValue = undefined;
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
max = [0, max];
}
const colorjsMax = Color.#colorjsMaxes[RGB][1];
const newval = map(new_green, max[0], max[1], colorjsMax[0], colorjsMax[1]);
- if(this.mode === RGB || this.mode === RGBP3){
+ if (this.mode === RGB || this.mode === RGBP3) {
this._color.coords[1] = newval;
- }else{
+ } else {
// Will do an imprecise conversion to 'srgb', not recommended
const space = this._color.space.id;
const representation = to(this._color, 'srgb');
@@ -652,18 +668,18 @@ class Color {
* describe('Two rectangles. The left one is salmon pink and the right one is pale fuchsia.');
* }
*/
- setBlue(new_blue, max=[0, 1]) {
+ setBlue(new_blue, max = [0, 1]) {
this._defaultStringValue = undefined;
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
max = [0, max];
}
const colorjsMax = Color.#colorjsMaxes[RGB][2];
const newval = map(new_blue, max[0], max[1], colorjsMax[0], colorjsMax[1]);
- if(this.mode === RGB || this.mode === RGBP3){
+ if (this.mode === RGB || this.mode === RGBP3) {
this._color.coords[2] = newval;
- }else{
+ } else {
// Will do an imprecise conversion to 'srgb', not recommended
const space = this._color.space.id;
const representation = to(this._color, 'srgb');
@@ -705,9 +721,9 @@ class Color {
* describe('Two rectangles. The left one is salmon pink and the right one is faded pink.');
* }
*/
- setAlpha(new_alpha, max=[0, 1]) {
+ setAlpha(new_alpha, max = [0, 1]) {
this._defaultStringValue = undefined;
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
max = [0, max];
}
@@ -717,7 +733,7 @@ class Color {
this._color.alpha = newval;
}
- _getRGBA(maxes=[1, 1, 1, 1]) {
+ _getRGBA(maxes = [1, 1, 1, 1]) {
// Get colorjs maxes
const colorjsMaxes = Color.#colorjsMaxes[RGB];
@@ -725,10 +741,10 @@ class Color {
let coords = structuredClone(to(this._color, 'srgb').coords);
coords.push(this._color.alpha);
- const rangeMaxes = maxes.map((v) => {
- if(!Array.isArray(v)){
+ const rangeMaxes = maxes.map(v => {
+ if (!Array.isArray(v)) {
return [0, v];
- }else{
+ } else {
return v;
}
});
@@ -736,8 +752,10 @@ class Color {
coords = coords.map((coord, i) => {
return map(
coord,
- colorjsMaxes[i][0], colorjsMaxes[i][1],
- rangeMaxes[i][0], rangeMaxes[i][1]
+ colorjsMaxes[i][0],
+ colorjsMaxes[i][1],
+ rangeMaxes[i][0],
+ rangeMaxes[i][1]
);
});
@@ -748,22 +766,30 @@ class Color {
return this.mode;
}
- _getRed(max=[0, 1]) {
- if(!Array.isArray(max)){
+ _getRed(max = [0, 1]) {
+ if (!Array.isArray(max)) {
max = [0, max];
}
- if(this.mode === RGB || this.mode === RGBP3){
+ if (this.mode === RGB || this.mode === RGBP3) {
const colorjsMax = Color.#colorjsMaxes[this.mode][0];
return map(
this._color.coords[0],
- colorjsMax[0], colorjsMax[1],
- max[0], max[1]
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
);
- }else{
+ } else {
// Will do an imprecise conversion to 'srgb', not recommended
const colorjsMax = Color.#colorjsMaxes[RGB][0];
- return map(to(this._color, 'srgb').coords[0], colorjsMax[0], colorjsMax[1], max[0], max[1]);
+ return map(
+ to(this._color, 'srgb').coords[0],
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
+ );
}
}
@@ -772,46 +798,62 @@ class Color {
* returns it in the range 0–255 by default. When `colorMode()` is given to an
* RBG value, the green value within the givin range is returned
*/
- _getGreen(max=[0, 1]) {
- if(!Array.isArray(max)){
+ _getGreen(max = [0, 1]) {
+ if (!Array.isArray(max)) {
max = [0, max];
}
- if(this.mode === RGB || this.mode === RGBP3){
+ if (this.mode === RGB || this.mode === RGBP3) {
const colorjsMax = Color.#colorjsMaxes[this.mode][1];
return map(
this._color.coords[1],
- colorjsMax[0], colorjsMax[1],
- max[0], max[1]
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
);
- }else{
+ } else {
// Will do an imprecise conversion to 'srgb', not recommended
const colorjsMax = Color.#colorjsMaxes[RGB][1];
- return map(to(this._color, 'srgb').coords[1], colorjsMax[0], colorjsMax[1], max[0], max[1]);
+ return map(
+ to(this._color, 'srgb').coords[1],
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
+ );
}
}
- _getBlue(max=[0, 1]) {
- if(!Array.isArray(max)){
+ _getBlue(max = [0, 1]) {
+ if (!Array.isArray(max)) {
max = [0, max];
}
- if(this.mode === RGB || this.mode === RGBP3){
+ if (this.mode === RGB || this.mode === RGBP3) {
const colorjsMax = Color.#colorjsMaxes[this.mode][2];
return map(
this._color.coords[2],
- colorjsMax[0], colorjsMax[1],
- max[0], max[1]
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
);
- }else{
+ } else {
// Will do an imprecise conversion to 'srgb', not recommended
const colorjsMax = Color.#colorjsMaxes[RGB][2];
- return map(to(this._color, 'srgb').coords[2], colorjsMax[0], colorjsMax[1], max[0], max[1]);
+ return map(
+ to(this._color, 'srgb').coords[2],
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
+ );
}
}
- _getAlpha(max=[0, 1]) {
- if(!Array.isArray(max)){
+ _getAlpha(max = [0, 1]) {
+ if (!Array.isArray(max)) {
max = [0, max];
}
@@ -825,22 +867,30 @@ class Color {
* an HSB color object, but will default to the HSL-normalized saturation
* otherwise.
*/
- _getHue(max=[0, 360]) {
- if(!Array.isArray(max)){
+ _getHue(max = [0, 360]) {
+ if (!Array.isArray(max)) {
max = [0, max];
}
- if(this.mode === HSB || this.mode === HSL){
+ if (this.mode === HSB || this.mode === HSL) {
const colorjsMax = Color.#colorjsMaxes[this.mode][0];
return map(
this._color.coords[0],
- colorjsMax[0], colorjsMax[1],
- max[0], max[1]
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
);
- }else{
+ } else {
// Will do an imprecise conversion to 'HSL', not recommended
const colorjsMax = Color.#colorjsMaxes[HSL][0];
- return map(to(this._color, 'hsl').coords[0], colorjsMax[0], colorjsMax[1], max[0], max[1]);
+ return map(
+ to(this._color, 'hsl').coords[0],
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
+ );
}
}
@@ -849,22 +899,30 @@ class Color {
* the HSB saturation when supplied with an HSB color object, but will default
* to the HSL saturation otherwise.
*/
- _getSaturation(max=[0, 100]) {
- if(!Array.isArray(max)){
+ _getSaturation(max = [0, 100]) {
+ if (!Array.isArray(max)) {
max = [0, max];
}
- if(this.mode === HSB || this.mode === HSL){
+ if (this.mode === HSB || this.mode === HSL) {
const colorjsMax = Color.#colorjsMaxes[this.mode][1];
return map(
this._color.coords[1],
- colorjsMax[0], colorjsMax[1],
- max[0], max[1]
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
);
- }else{
+ } else {
// Will do an imprecise conversion to 'HSL', not recommended
const colorjsMax = Color.#colorjsMaxes[HSL][1];
- return map(to(this._color, 'hsl').coords[1], colorjsMax[0], colorjsMax[1], max[0], max[1]);
+ return map(
+ to(this._color, 'hsl').coords[1],
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
+ );
}
}
@@ -875,46 +933,62 @@ class Color {
* brightness value in the range. By default, this function will return
* the HSB brightness within the range 0 - 100.
*/
- _getBrightness(max=[0, 100]) {
- if(!Array.isArray(max)){
+ _getBrightness(max = [0, 100]) {
+ if (!Array.isArray(max)) {
max = [0, max];
}
- if(this.mode === HSB){
+ if (this.mode === HSB) {
const colorjsMax = Color.#colorjsMaxes[this.mode][2];
return map(
this._color.coords[2],
- colorjsMax[0], colorjsMax[1],
- max[0], max[1]
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
);
- }else{
+ } else {
// Will do an imprecise conversion to 'HSB', not recommended
const colorjsMax = Color.#colorjsMaxes[HSB][2];
- return map(to(this._color, 'hsb').coords[2], colorjsMax[0], colorjsMax[1], max[0], max[1]);
+ return map(
+ to(this._color, 'hsb').coords[2],
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
+ );
}
}
- _getLightness(max=[0, 100]) {
- if(!Array.isArray(max)){
+ _getLightness(max = [0, 100]) {
+ if (!Array.isArray(max)) {
max = [0, max];
}
- if(this.mode === HSL){
+ if (this.mode === HSL) {
const colorjsMax = Color.#colorjsMaxes[this.mode][2];
return map(
this._color.coords[2],
- colorjsMax[0], colorjsMax[1],
- max[0], max[1]
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
);
- }else{
+ } else {
// Will do an imprecise conversion to 'HSL', not recommended
const colorjsMax = Color.#colorjsMaxes[HSL][2];
- return map(to(this._color, 'hsl').coords[2], colorjsMax[0], colorjsMax[1], max[0], max[1]);
+ return map(
+ to(this._color, 'hsl').coords[2],
+ colorjsMax[0],
+ colorjsMax[1],
+ max[0],
+ max[1]
+ );
}
}
}
-function color(p5, fn, lifecycles){
+function color(p5, fn, lifecycles) {
/**
* A class to describe a color.
*
@@ -948,12 +1022,12 @@ function color(p5, fn, lifecycles){
*/
p5.Color = Color;
- sRGB.fromGray = P3.fromGray = function(val, maxes, clamp){
+ sRGB.fromGray = P3.fromGray = function (val, maxes, clamp) {
// Use blue max
const p5Maxes = maxes.map(max => {
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
return [0, max];
- }else{
+ } else {
return max;
}
});
@@ -962,12 +1036,12 @@ function color(p5, fn, lifecycles){
return [v, v, v];
};
- HSBSpace.fromGray = HSLSpace.fromGray = function(val, maxes, clamp){
+ HSBSpace.fromGray = HSLSpace.fromGray = function (val, maxes, clamp) {
// Use brightness max
const p5Maxes = maxes.map(max => {
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
return [0, max];
- }else{
+ } else {
return max;
}
});
@@ -976,26 +1050,26 @@ function color(p5, fn, lifecycles){
return [0, 0, v];
};
- HWBSpace.fromGray = function(val, maxes, clamp){
+ HWBSpace.fromGray = function (val, maxes, clamp) {
// Use Whiteness and Blackness to create number line
const p5Maxes = maxes.map(max => {
- if(!Array.isArray(max)){
+ if (!Array.isArray(max)) {
return [0, max];
- }else{
+ } else {
return max;
}
});
const wbMax =
- (Math.abs(p5Maxes[1][0] - p5Maxes[1][1])) / 2 +
- (Math.abs(p5Maxes[2][0] - p5Maxes[2][1])) / 2;
+ Math.abs(p5Maxes[1][0] - p5Maxes[1][1]) / 2 +
+ Math.abs(p5Maxes[2][0] - p5Maxes[2][1]) / 2;
const nVal = map(val, 0, wbMax, 0, 100);
let white, black;
- if(nVal < 50){
+ if (nVal < 50) {
black = nVal;
white = 100 - nVal;
- }else if(nVal >= 50){
+ } else if (nVal >= 50) {
white = nVal;
black = 100 - nVal;
}
@@ -1003,22 +1077,22 @@ function color(p5, fn, lifecycles){
};
Lab.fromGray =
- LCHSpace.fromGray =
- OKLab.fromGray =
- OKLCHSpace.fromGray =
- function(val, maxes, clamp){
- // Use lightness max
- const p5Maxes = maxes.map(max => {
- if(!Array.isArray(max)){
- return [0, max];
- }else{
- return max;
- }
- });
+ LCHSpace.fromGray =
+ OKLab.fromGray =
+ OKLCHSpace.fromGray =
+ function (val, maxes, clamp) {
+ // Use lightness max
+ const p5Maxes = maxes.map(max => {
+ if (!Array.isArray(max)) {
+ return [0, max];
+ } else {
+ return max;
+ }
+ });
- const v = map(val, p5Maxes[0][0], p5Maxes[0][1], 0, 100, clamp);
- return [v, 0, 0];
- };
+ const v = map(val, p5Maxes[0][0], p5Maxes[0][1], 0, 100, clamp);
+ return [v, 0, 0];
+ };
// Register color modes and initialize Color maxes to what p5 has set for itself
p5.Color.addColorMode(RGB, sRGB);
@@ -1031,35 +1105,35 @@ function color(p5, fn, lifecycles){
p5.Color.addColorMode(OKLAB, OKLab);
p5.Color.addColorMode(OKLCH, OKLCHSpace);
- lifecycles.presetup = function(){
+ lifecycles.presetup = function () {
const pInst = this;
// Decorate set methods
const setMethods = ['Red', 'Green', 'Blue', 'Alpha'];
- for(let i in setMethods){
+ for (let i in setMethods) {
const method = setMethods[i];
const setCopy = p5.Color.prototype['set' + method];
- p5.Color.prototype['set' + method] = function(newval, max){
+ p5.Color.prototype['set' + method] = function (newval, max) {
max = max || pInst?._renderer?.states?.colorMaxes?.[RGB][i];
return setCopy.call(this, newval, max);
};
}
// Decorate get methods
- function decorateGet(channel, modes){
+ function decorateGet(channel, modes) {
const getCopy = p5.Color.prototype['_get' + channel];
- p5.Color.prototype['_get' + channel] = function(max){
- if(Object.keys(modes).includes(this.mode)){
- max = max ||
+ p5.Color.prototype['_get' + channel] = function (max) {
+ if (Object.keys(modes).includes(this.mode)) {
+ max =
+ max ||
pInst?._renderer?.states?.colorMaxes?.[this.mode][modes[this.mode]];
- }else{
+ } else {
const defaultMode = Object.keys(modes)[0];
- max = max ||
- pInst
- ?._renderer
- ?.states
- ?.colorMaxes
- ?.[defaultMode][modes[defaultMode]];
+ max =
+ max ||
+ pInst?._renderer?.states?.colorMaxes?.[defaultMode][
+ modes[defaultMode]
+ ];
}
return getCopy.call(this, max);
@@ -1113,6 +1187,6 @@ function color(p5, fn, lifecycles){
export default color;
export { Color };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
color(p5, p5.prototype);
}
diff --git a/src/color/setting.js b/src/color/setting.js
index cf4310fed8..4c1a44a8da 100644
--- a/src/color/setting.js
+++ b/src/color/setting.js
@@ -5,9 +5,19 @@
*/
import * as constants from '../core/constants';
-import { RGB, RGBP3, HSL, HSB, HWB, LAB, LCH, OKLAB, OKLCH } from './creating_reading';
+import {
+ RGB,
+ RGBP3,
+ HSL,
+ HSB,
+ HWB,
+ LAB,
+ LCH,
+ OKLAB,
+ OKLCH
+} from './creating_reading';
-function setting(p5, fn){
+function setting(p5, fn) {
/**
* Starts defining a shape that will mask any shapes drawn afterward.
*
@@ -163,7 +173,7 @@ function setting(p5, fn){
* endShape();
* }
*/
- fn.beginClip = function(options = {}) {
+ fn.beginClip = function (options = {}) {
this._renderer.beginClip(options);
};
@@ -191,7 +201,7 @@ function setting(p5, fn){
* describe('A white triangle and circle on a gray background.');
* }
*/
- fn.endClip = function() {
+ fn.endClip = function () {
this._renderer.endClip();
};
@@ -363,7 +373,7 @@ function setting(p5, fn){
* pop();
* }
*/
- fn.clip = function(callback, options) {
+ fn.clip = function (callback, options) {
this._renderer.beginClip(options);
callback();
this._renderer.endClip(options);
@@ -577,7 +587,7 @@ function setting(p5, fn){
* @param {Number} [a]
* @chainable
*/
- fn.background = function(...args) {
+ fn.background = function (...args) {
return this._renderer.background(...args);
};
@@ -642,7 +652,7 @@ function setting(p5, fn){
* @param {Number} [b] normalized blue value.
* @param {Number} [a] normalized alpha value.
*/
- fn.clear = function(...args) {
+ fn.clear = function (...args) {
const _r = args[0] || 0;
const _g = args[1] || 0;
const _b = args[2] || 0;
@@ -1031,26 +1041,17 @@ function setting(p5, fn){
* @method colorMode
* @return {RGB|HSB|HSL|RGBP3|HWB|LAB|LCH|OKLAB|OKLCH} The current color mode.
*/
- fn.colorMode = function(mode, max1, max2, max3, maxA) {
+ fn.colorMode = function (mode, max1, max2, max3, maxA) {
// p5._validateParameters('colorMode', arguments);
- if (
- [
- RGB,
- RGBP3,
- HSB,
- HSL,
- HWB,
- LAB,
- LCH,
- OKLAB,
- OKLCH
- ].includes(mode)
- ) {
+ if ([RGB, RGBP3, HSB, HSL, HWB, LAB, LCH, OKLAB, OKLCH].includes(mode)) {
// Set color mode.
this._renderer.states.setValue('colorMode', mode);
// Set color maxes.
- this._renderer.states.setValue('colorMaxes', this._renderer.states.colorMaxes.clone());
+ this._renderer.states.setValue(
+ 'colorMaxes',
+ this._renderer.states.colorMaxes.clone()
+ );
const maxes = this._renderer.states.colorMaxes[mode];
if (arguments.length === 2) {
maxes[0] = max1; // Red
@@ -1287,7 +1288,7 @@ function setting(p5, fn){
* @method fill
* @return {p5.Color} the current fill color.
*/
- fn.fill = function(...args) {
+ fn.fill = function (...args) {
return this._renderer.fill(...args);
};
@@ -1339,7 +1340,7 @@ function setting(p5, fn){
* box(45);
* }
*/
- fn.noFill = function() {
+ fn.noFill = function () {
this._renderer.noFill();
return this;
};
@@ -1388,7 +1389,7 @@ function setting(p5, fn){
* box(45);
* }
*/
- fn.noStroke = function() {
+ fn.noStroke = function () {
this._renderer.states.setValue('strokeColor', null);
return this;
};
@@ -1609,7 +1610,7 @@ function setting(p5, fn){
* @method stroke
* @return {p5.Color} the current stroke color.
*/
- fn.stroke = function(...args) {
+ fn.stroke = function (...args) {
return this._renderer.stroke(...args);
};
@@ -1686,7 +1687,7 @@ function setting(p5, fn){
* describe('A purple canvas with a pink square in the middle. A circle at the top-left partially erases its interior and a fully erases its outline.');
* }
*/
- fn.erase = function(opacityFill = 255, opacityStroke = 255) {
+ fn.erase = function (opacityFill = 255, opacityStroke = 255) {
this._renderer.erase(opacityFill, opacityStroke);
return this;
@@ -1723,7 +1724,7 @@ function setting(p5, fn){
* describe('An orange canvas with two tall blue rectangles. A circular hole in the center erases the rectangle on the left but not the one on the right.');
* }
*/
- fn.noErase = function() {
+ fn.noErase = function () {
this._renderer.noErase();
return this;
};
@@ -2164,6 +2165,6 @@ function setting(p5, fn){
export default setting;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
setting(p5, p5.prototype);
}
diff --git a/src/core/README.md b/src/core/README.md
index 49f5e3cadf..f9e009a4b3 100644
--- a/src/core/README.md
+++ b/src/core/README.md
@@ -1,13 +1,12 @@
# p5.js Core
-This folder contains the core functionality of p5.js, including the init and main files, environment, constants, the friendly error system, and the graphics pipeline (with the exception of 3D/WebGL). [Here is a diagram](
-https://sketchboard.me/zBOkvPuQisyA#/) that outlines how the main graphics-related classes inherit from and extend one another. There is still more to be done to document this pipeline more clearly.
+This folder contains the core functionality of p5.js, including the init and main files, environment, constants, the friendly error system, and the graphics pipeline (with the exception of 3D/WebGL). [Here is a diagram](https://sketchboard.me/zBOkvPuQisyA#/) that outlines how the main graphics-related classes inherit from and extend one another. There is still more to be done to document this pipeline more clearly.

## [init.js](./init.js)
-The `init.js` module initializes a new `p5` object in either global mode, instance mode, or unit testing mode. Broadly speaking, this module should only contain logic that needs to execute *before startup*, such as testing the environment in which the code is running in order to make decisions about how it should load.
+The `init.js` module initializes a new `p5` object in either global mode, instance mode, or unit testing mode. Broadly speaking, this module should only contain logic that needs to execute _before startup_, such as testing the environment in which the code is running in order to make decisions about how it should load.
## [main.js](./main.js)
@@ -17,25 +16,27 @@ Aside from the exported constructor function, the most important thing in this m
## [structure.js](./structure.js)
-The `structure.js` module is best thought of as a collection of ways to make *modifications* to the typical *loop structure* exposed by the `draw()` function. The functions in this module allow you to add and extract individual renders from the loop, draw the loop one frame at a time, pause and resume the loop, and so on.
+The `structure.js` module is best thought of as a collection of ways to make _modifications_ to the typical _loop structure_ exposed by the `draw()` function. The functions in this module allow you to add and extract individual renders from the loop, draw the loop one frame at a time, pause and resume the loop, and so on.
## [constants.js](./constants.js)
The `constants.js` module provides various default values mostly related to math and web standards using clear semantic names to increase syntax clarity when they are used in other algorithms. These values are attached to the `p5` object prototype as properties by the constructor function so they can be more easily accessed by other parts of the p5.js library or by user code.
## [friendly_errors/fes_core.js](./friendly_errors/fes_core.js)
-This is the main file for the Friendly Error System (FES). The Friendly Error System (FES) is a system designed to help new programmers with common user errors as they get started learning. It catches common beginning errors and provides clear language and links to help a user resolve the error. FES is only applied to functions that are ones a user might encounter when they are just starting. You can learn more about the FES in the comment section at the top of this file, or in the [contributors_docs/friendly_error_system.md](https://github.com/processing/p5.js/blob/main/contributor_docs/friendly_error_system.md) file.
+
+This is the main file for the Friendly Error System (FES). The Friendly Error System (FES) is a system designed to help new programmers with common user errors as they get started learning. It catches common beginning errors and provides clear language and links to help a user resolve the error. FES is only applied to functions that are ones a user might encounter when they are just starting. You can learn more about the FES in the comment section at the top of this file, or in the [contributors_docs/friendly_error_system.md](https://github.com/processing/p5.js/blob/main/contributor_docs/friendly_error_system.md) file.
## [legacy.js](./legacy.js)
+
These are functions that are part of the Processing API but are not part of the p5.js API. In some cases they have a new name, in others, they are removed completely. Not all unsupported Processing functions are listed here but we try to include ones that a user coming from Processing might likely call.
# Browser
-*utilizing and abstracting web technologies*
+_utilizing and abstracting web technologies_
## [environment.js](./environment.js)
-The `environment.js` module detects and semantically reassigns various features of the code execution environment that are arguably beyond the control of the p5.js library, such as device properties, window dimensions, and the URL bar. In general, this module should only contain code that exposes mechanics of the web that usually would not be considered part of the p5.js "sketch." This module is conceptually similar to `init.js` in that it tests the environment in which the p5.js code is executing, but one major difference is that broadly speaking the code in `environment.js` does *not* need to run solely at initialization as with `init.js`, and can be invoked whenever necessary.
+The `environment.js` module detects and semantically reassigns various features of the code execution environment that are arguably beyond the control of the p5.js library, such as device properties, window dimensions, and the URL bar. In general, this module should only contain code that exposes mechanics of the web that usually would not be considered part of the p5.js "sketch." This module is conceptually similar to `init.js` in that it tests the environment in which the p5.js code is executing, but one major difference is that broadly speaking the code in `environment.js` does _not_ need to run solely at initialization as with `init.js`, and can be invoked whenever necessary.
## [shim.js](./shim.js)
@@ -43,11 +44,11 @@ Sometimes p5.js needs to use new browser APIs or other features which haven't ye
## [p5.Element.js](./p5.Element.js)
-Although p5.js is a general toolkit for all sorts of interactive art projects, in practice many p5.js sketches focus on rendering two dimensional graphics into a `
*
*/
- fn.instances = function(count) {
+ fn.instances = function (count) {
this._assert3d('instances');
if (typeof count !== 'number' || !isFinite(count) || count < 1) {
@@ -2683,33 +2711,34 @@ function primitives3D(p5, fn){
// Each wrapped method: set _instanceCount, call the method with
// the correct context, clear _instanceCount in finally so it never leaks.
- const wrap = (method, ctx = r) => function(...args) {
- r._instanceCount = count;
- try {
- method.apply(ctx, args);
- } finally {
- r._instanceCount = undefined;
- }
- };
+ const wrap = (method, ctx = r) =>
+ function (...args) {
+ r._instanceCount = count;
+ try {
+ method.apply(ctx, args);
+ } finally {
+ r._instanceCount = undefined;
+ }
+ };
const result = {
- sphere: wrap(r.sphere),
- box: wrap(r.box),
- plane: wrap(r.plane),
+ sphere: wrap(r.sphere),
+ box: wrap(r.box),
+ plane: wrap(r.plane),
ellipsoid: wrap(r.ellipsoid),
- cylinder: wrap(r.cylinder),
- cone: wrap(r.cone),
- torus: wrap(r.torus),
- triangle: wrap(this.triangle, this),
- rect: wrap(this.rect, this),
- quad: wrap(this.quad, this),
- ellipse: wrap(this.ellipse, this),
- arc: wrap(this.arc, this),
- model: wrap(r.model),
- line: wrap(this.line, this),
- point: wrap(this.point, this),
- bezier: wrap(this.bezier, this),
- spline: wrap(this.spline, this)
+ cylinder: wrap(r.cylinder),
+ cone: wrap(r.cone),
+ torus: wrap(r.torus),
+ triangle: wrap(this.triangle, this),
+ rect: wrap(this.rect, this),
+ quad: wrap(this.quad, this),
+ ellipse: wrap(this.ellipse, this),
+ arc: wrap(this.arc, this),
+ model: wrap(r.model),
+ line: wrap(this.line, this),
+ point: wrap(this.point, this),
+ bezier: wrap(this.bezier, this),
+ spline: wrap(this.spline, this)
};
if (typeof this.curve === 'function') {
@@ -2722,6 +2751,6 @@ function primitives3D(p5, fn){
export default primitives3D;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
primitives3D(p5, p5.prototype);
}
diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js
index 777feb838e..b3b2d20216 100644
--- a/src/webgl/GeometryBuilder.js
+++ b/src/webgl/GeometryBuilder.js
@@ -54,8 +54,9 @@ class GeometryBuilder {
* transformations.
*/
addGeometry(input) {
- this.hasTransform = !this.renderer.states.uModelMatrix.mat4
- .every((v, i) => v === this.identityMatrix.mat4[i]);
+ this.hasTransform = !this.renderer.states.uModelMatrix.mat4.every(
+ (v, i) => v === this.identityMatrix.mat4[i]
+ );
if (this.hasTransform) {
this.renderer.scratchMat3.inverseTranspose4x4(
@@ -79,8 +80,8 @@ class GeometryBuilder {
const numPreviousVertices =
this.geometry.vertices.length - input.vertices.length;
- for (const propName in builtUserVertexProps){
- if (propName in inputUserVertexProps){
+ for (const propName in builtUserVertexProps) {
+ if (propName in inputUserVertexProps) {
continue;
}
const prop = builtUserVertexProps[propName];
@@ -89,11 +90,11 @@ class GeometryBuilder {
const missingValues = Array(numMissingValues).fill(0);
prop.pushDirect(missingValues);
}
- for (const propName in inputUserVertexProps){
+ for (const propName in inputUserVertexProps) {
const prop = inputUserVertexProps[propName];
const data = prop.getSrcArray();
const size = prop.getDataSize();
- if (numPreviousVertices > 0 && !(propName in builtUserVertexProps)){
+ if (numPreviousVertices > 0 && !(propName in builtUserVertexProps)) {
const numMissingValues = size * numPreviousVertices;
const missingValues = Array(numMissingValues).fill(0);
this.geometry.vertexProperty(propName, missingValues, size);
@@ -107,7 +108,9 @@ class GeometryBuilder {
);
}
if (this.renderer.states.strokeColor) {
- for (const edge of input.edges.map(edge => edge.map(idx => idx + startIdx))) {
+ for (const edge of input.edges.map(edge =>
+ edge.map(idx => idx + startIdx)
+ )) {
this.geometry.edges.push(edge);
}
}
@@ -147,8 +150,10 @@ class GeometryBuilder {
for (let i = 0; i < geometry.vertices.length; i += 3) {
if (
!validateFaces ||
- geometry.vertices[i].copy().sub(geometry.vertices[i+1])
- .cross(geometry.vertices[i].copy().sub(geometry.vertices[i+2]))
+ geometry.vertices[i]
+ .copy()
+ .sub(geometry.vertices[i + 1])
+ .cross(geometry.vertices[i].copy().sub(geometry.vertices[i + 2]))
.magSq() > 0
) {
faces.push([i, i + 1, i + 2]);
diff --git a/src/webgl/ShapeBuilder.js b/src/webgl/ShapeBuilder.js
index 03afb2105b..b5c9201e08 100644
--- a/src/webgl/ShapeBuilder.js
+++ b/src/webgl/ShapeBuilder.js
@@ -14,8 +14,9 @@ const INITIAL_BUFFER_STRIDES = {
// The total number of properties per vertex, before additional
// user attributes are added.
-const INITIAL_VERTEX_SIZE =
- Object.values(INITIAL_BUFFER_STRIDES).reduce((acc, next) => acc + next);
+const INITIAL_VERTEX_SIZE = Object.values(INITIAL_BUFFER_STRIDES).reduce(
+ (acc, next) => acc + next
+);
export class ShapeBuilder {
constructor(renderer) {
@@ -50,7 +51,7 @@ export class ShapeBuilder {
}
constructFromContours(shape, contours) {
- if (this._useUserVertexProperties){
+ if (this._useUserVertexProperties) {
this._resetUserVertexProperties();
}
this.geometry.reset();
@@ -162,10 +163,10 @@ export class ShapeBuilder {
) {
const proceed = window.confirm(
'🌸 p5.js says:\n\n' +
- `This shape has ${vertexCount} vertices. Tessellating shapes with this ` +
- 'many vertices can be very slow and may cause your browser to become ' +
- 'unresponsive.\n\n' +
- 'Do you want to continue tessellating this shape?'
+ `This shape has ${vertexCount} vertices. Tessellating shapes with this ` +
+ 'many vertices can be very slow and may cause your browser to become ' +
+ 'unresponsive.\n\n' +
+ 'Do you want to continue tessellating this shape?'
);
if (!proceed) {
return;
@@ -216,7 +217,9 @@ export class ShapeBuilder {
_remapVertices(newIndices) {
this.geometry.vertices = newIndices.map(i => this.geometry.vertices[i]);
- this.geometry.vertexNormals = newIndices.map(i => this.geometry.vertexNormals[i]);
+ this.geometry.vertexNormals = newIndices.map(
+ i => this.geometry.vertexNormals[i]
+ );
const remapFlat = (arr, stride) => {
const result = [];
@@ -230,7 +233,10 @@ export class ShapeBuilder {
this.geometry.uvs = remapFlat(this.geometry.uvs, 2);
this.geometry.vertexColors = remapFlat(this.geometry.vertexColors, 4);
- this.geometry.vertexStrokeColors = remapFlat(this.geometry.vertexStrokeColors, 4);
+ this.geometry.vertexStrokeColors = remapFlat(
+ this.geometry.vertexStrokeColors,
+ 4
+ );
for (const propName in this.geometry.userVertexProperties) {
const prop = this.geometry.userVertexProperties[propName];
@@ -256,7 +262,7 @@ export class ShapeBuilder {
_resetUserVertexProperties() {
const properties = this.geometry.userVertexProperties;
- for (const propName in properties){
+ for (const propName in properties) {
const prop = properties[propName];
delete this.bufferStrides[propName];
prop.delete();
@@ -272,10 +278,7 @@ export class ShapeBuilder {
* @private
* @returns {Number[]} indices for custom shape vertices indicating edges.
*/
- _calculateEdges(
- shapeMode,
- verts
- ) {
+ _calculateEdges(shapeMode, verts) {
const res = [];
let i = 0;
const contourIndices = this.contourIndices.slice();
@@ -357,14 +360,11 @@ export class ShapeBuilder {
// const contours = [[]];
const contours = [];
for (let i = 0; i < this.geometry.vertices.length; i++) {
- if (
- this.contourIndices.length > 0 &&
- this.contourIndices[0] === i
- ) {
+ if (this.contourIndices.length > 0 && this.contourIndices[0] === i) {
this.contourIndices.shift();
contours.push([]);
}
- contours[contours.length-1].push(
+ contours[contours.length - 1].push(
this.geometry.vertices[i].x,
this.geometry.vertices[i].y,
this.geometry.vertices[i].z,
@@ -383,7 +383,7 @@ export class ShapeBuilder {
const start = i * prop.getDataSize();
const end = start + prop.getDataSize();
const vals = prop.getSrcArray().slice(start, end);
- contours[contours.length-1].push(...vals);
+ contours[contours.length - 1].push(...vals);
}
}
@@ -422,7 +422,7 @@ export class ShapeBuilder {
this.geometry.vertices = [];
this.geometry.vertexNormals = [];
this.geometry.uvs = [];
- for (const propName in this.geometry.userVertexProperties){
+ for (const propName in this.geometry.userVertexProperties) {
const prop = this.geometry.userVertexProperties[propName];
prop.resetSrcArray();
}
@@ -438,7 +438,7 @@ export class ShapeBuilder {
);
{
let offset = 12;
- for (const propName in this.geometry.userVertexProperties){
+ for (const propName in this.geometry.userVertexProperties) {
const prop = this.geometry.userVertexProperties[propName];
const size = prop.getDataSize();
const start = j + offset;
@@ -461,15 +461,12 @@ export class ShapeBuilder {
// We record index mappings in a Map so that once we have found a
// corresponding vertex, we don't need to loop to find it again.
const newIndex = new Map();
- this.geometry.edges =
- this.geometry.edges.map(edge => edge.map(origIdx => {
+ this.geometry.edges = this.geometry.edges.map(edge =>
+ edge.map(origIdx => {
if (!newIndex.has(origIdx)) {
const orig = originalVertices[origIdx];
let newVertIndex = this.geometry.vertices.findIndex(
- v =>
- orig.x === v.x &&
- orig.y === v.y &&
- orig.z === v.z
+ v => orig.x === v.x && orig.y === v.y && orig.z === v.z
);
if (newVertIndex === -1) {
// The tesselation process didn't output a vertex with the exact
@@ -477,16 +474,12 @@ export class ShapeBuilder {
// doesn't happen often, but in this case, pick the closest point
let closestDist = Infinity;
let closestIndex = 0;
- for (
- let i = 0;
- i < this.geometry.vertices.length;
- i++
- ) {
+ for (let i = 0; i < this.geometry.vertices.length; i++) {
const vert = this.geometry.vertices[i];
const dX = orig.x - vert.x;
const dY = orig.y - vert.y;
const dZ = orig.z - vert.z;
- const dist = dX*dX + dY*dY + dZ*dZ;
+ const dist = dX * dX + dY * dY + dZ * dZ;
if (dist < closestDist) {
closestDist = dist;
closestIndex = i;
@@ -497,7 +490,8 @@ export class ShapeBuilder {
newIndex.set(origIdx, newVertIndex);
}
return newIndex.get(origIdx);
- }));
+ })
+ );
}
this.geometry.vertexColors = colors;
}
@@ -564,11 +558,7 @@ export class ShapeBuilder {
const z = contours[0] ? contours[0][2] : undefined;
let allSameZ = true;
for (const contour of contours) {
- for (
- let j = 0;
- j < contour.length;
- j += this.tessyVertexSize
- ) {
+ for (let j = 0; j < contour.length; j += this.tessyVertexSize) {
if (contour[j + 2] !== z) {
allSameZ = false;
break;
@@ -587,15 +577,8 @@ export class ShapeBuilder {
for (const contour of contours) {
this._tessy.gluTessBeginContour();
- for (
- let j = 0;
- j < contour.length;
- j += this.tessyVertexSize
- ) {
- const coords = contour.slice(
- j,
- j + this.tessyVertexSize
- );
+ for (let j = 0; j < contour.length; j += this.tessyVertexSize) {
+ const coords = contour.slice(j, j + this.tessyVertexSize);
this._tessy.gluTessVertex(coords, coords);
}
this._tessy.gluTessEndContour();
@@ -606,4 +589,4 @@ export class ShapeBuilder {
return triangleVerts;
}
-};
+}
diff --git a/src/webgl/enums.js b/src/webgl/enums.js
index a93e3dc914..531c9465d3 100644
--- a/src/webgl/enums.js
+++ b/src/webgl/enums.js
@@ -2,7 +2,7 @@ import * as constants from '../core/constants';
export function getStrokeDefs(shaderConstant) {
const STROKE_CAP_ENUM = {};
const STROKE_JOIN_ENUM = {};
- let lineDefs = "";
+ let lineDefs = '';
const defineStrokeCapEnum = function (key, val) {
lineDefs += shaderConstant(`STROKE_CAP_${key}`, `${val}`, 'u32');
STROKE_CAP_ENUM[constants[key]] = val;
@@ -14,12 +14,12 @@ export function getStrokeDefs(shaderConstant) {
// Define constants in line shaders for each type of cap/join, and also record
// the values in JS objects
- defineStrokeCapEnum("ROUND", 0);
- defineStrokeCapEnum("PROJECT", 1);
- defineStrokeCapEnum("SQUARE", 2);
- defineStrokeJoinEnum("ROUND", 0);
- defineStrokeJoinEnum("MITER", 1);
- defineStrokeJoinEnum("BEVEL", 2);
+ defineStrokeCapEnum('ROUND', 0);
+ defineStrokeCapEnum('PROJECT', 1);
+ defineStrokeCapEnum('SQUARE', 2);
+ defineStrokeJoinEnum('ROUND', 0);
+ defineStrokeJoinEnum('MITER', 1);
+ defineStrokeJoinEnum('BEVEL', 2);
return { STROKE_CAP_ENUM, STROKE_JOIN_ENUM, lineDefs };
}
diff --git a/src/webgl/index.js b/src/webgl/index.js
index 9bf7a3c353..34a7185055 100644
--- a/src/webgl/index.js
+++ b/src/webgl/index.js
@@ -15,7 +15,7 @@ import texture from './p5.Texture';
import renderer3D from '../core/p5.Renderer3D';
import rendererGL from './p5.RendererGL';
-export default function(p5){
+export default function (p5) {
p5.registerAddon(renderer3D);
p5.registerAddon(rendererGL);
p5.registerAddon(primitives3D);
diff --git a/src/webgl/interaction.js b/src/webgl/interaction.js
index 669ff75553..b1ddca57ce 100644
--- a/src/webgl/interaction.js
+++ b/src/webgl/interaction.js
@@ -7,7 +7,7 @@
import * as constants from '../core/constants';
import { Vector } from '../math/p5.Vector';
-function interaction(p5, fn){
+function interaction(p5, fn) {
/**
* Allows the user to orbit around a 3D sketch using a mouse, trackpad, or
* touchscreen.
@@ -151,7 +151,7 @@ function interaction(p5, fn){
// implementation based on three.js 'orbitControls':
// https://github.com/mrdoob/three.js/blob/6afb8595c0bf8b2e72818e42b64e6fe22707d896/examples/jsm/controls/OrbitControls.js#L22
- fn.orbitControl = function(
+ fn.orbitControl = function (
sensitivityX,
sensitivityY,
sensitivityZ,
@@ -253,13 +253,15 @@ function interaction(p5, fn){
// for touch, it is calculated based on one moved touch pointer position.
pointersInCanvas =
- movedTouches[0].x > 0 && movedTouches[0].x < this.width &&
- movedTouches[0].y > 0 && movedTouches[0].y < this.height;
+ movedTouches[0].x > 0 &&
+ movedTouches[0].x < this.width &&
+ movedTouches[0].y > 0 &&
+ movedTouches[0].y < this.height;
if (movedTouches.length === 1) {
const t = movedTouches[0];
- deltaTheta = -sensitivityX * (t.x - t.px) / scaleFactor;
- deltaPhi = sensitivityY * (t.y - t.py) / scaleFactor;
+ deltaTheta = (-sensitivityX * (t.x - t.px)) / scaleFactor;
+ deltaPhi = (sensitivityY * (t.y - t.py)) / scaleFactor;
} else {
const t0 = movedTouches[0];
const t1 = movedTouches[1];
@@ -293,8 +295,10 @@ function interaction(p5, fn){
// For mouse, it is calculated based on the mouse position.
pointersInCanvas =
- (this.mouseX > 0 && this.mouseX < this.width) &&
- (this.mouseY > 0 && this.mouseY < this.height);
+ this.mouseX > 0 &&
+ this.mouseX < this.width &&
+ this.mouseY > 0 &&
+ this.mouseY < this.height;
if (this._mouseWheelDeltaY !== 0) {
// zoom the camera depending on the value of _mouseWheelDeltaY.
@@ -310,11 +314,11 @@ function interaction(p5, fn){
}
if (this.mouseIsPressed) {
if (this.mouseButton.left) {
- deltaTheta = -sensitivityX * this.movedX / scaleFactor;
- deltaPhi = sensitivityY * this.movedY / scaleFactor;
+ deltaTheta = (-sensitivityX * this.movedX) / scaleFactor;
+ deltaPhi = (sensitivityY * this.movedY) / scaleFactor;
} else if (this.mouseButton.right) {
moveDeltaX = this.movedX;
- moveDeltaY = this.movedY * cam.yScale;
+ moveDeltaY = this.movedY * cam.yScale;
}
// start rotate and move when mouse is pressed within the canvas.
if (pointersInCanvas) this._renderer.executeRotateAndMove = true;
@@ -334,26 +338,21 @@ function interaction(p5, fn){
if (Math.abs(this._renderer.zoomVelocity) > 0.001) {
// if freeRotation is true, we use _orbitFree() instead of _orbit()
if (freeRotation) {
- cam._orbitFree(
- 0, 0, this._renderer.zoomVelocity
- );
+ cam._orbitFree(0, 0, this._renderer.zoomVelocity);
} else {
- cam._orbit(
- 0, 0, this._renderer.zoomVelocity
- );
+ cam._orbit(0, 0, this._renderer.zoomVelocity);
}
// In orthogonal projection, the scale does not change even if
// the distance to the gaze point is changed, so the projection matrix
// needs to be modified.
if (cam.projMatrix.mat4[15] !== 0) {
- cam.projMatrix.mat4[0] *= Math.pow(
- 10, -this._renderer.zoomVelocity
- );
- cam.projMatrix.mat4[5] *= Math.pow(
- 10, -this._renderer.zoomVelocity
- );
+ cam.projMatrix.mat4[0] *= Math.pow(10, -this._renderer.zoomVelocity);
+ cam.projMatrix.mat4[5] *= Math.pow(10, -this._renderer.zoomVelocity);
// modify uPMatrix
- this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone());
+ this._renderer.states.setValue(
+ 'uPMatrix',
+ this._renderer.states.uPMatrix.clone()
+ );
this._renderer.states.uPMatrix.mat4[0] = cam.projMatrix.mat4[0];
this._renderer.states.uPMatrix.mat4[5] = cam.projMatrix.mat4[5];
}
@@ -364,8 +363,10 @@ function interaction(p5, fn){
}
// rotate process
- if ((deltaTheta !== 0 || deltaPhi !== 0) &&
- this._renderer.executeRotateAndMove) {
+ if (
+ (deltaTheta !== 0 || deltaPhi !== 0) &&
+ this._renderer.executeRotateAndMove
+ ) {
// accelerate rotate velocity
this._renderer.rotateVelocity.add(
deltaTheta * rotateAccelerationFactor,
@@ -393,17 +394,18 @@ function interaction(p5, fn){
// damping
this._renderer.rotateVelocity.mult(damping);
//console.log("multiplied", damping, this._renderer.rotateVelocity);
-
} else {
this._renderer.rotateVelocity.set(0, 0, 0);
}
// move process
- if ((moveDeltaX !== 0 || moveDeltaY !== 0) &&
- this._renderer.executeRotateAndMove) {
+ if (
+ (moveDeltaX !== 0 || moveDeltaY !== 0) &&
+ this._renderer.executeRotateAndMove
+ ) {
// Normalize movement distance
- const ndcX = moveDeltaX * 2/this.width;
- const ndcY = -moveDeltaY * 2/this.height;
+ const ndcX = (moveDeltaX * 2) / this.width;
+ const ndcY = (-moveDeltaY * 2) / this.height;
// accelerate move velocity
this._renderer.moveVelocity.add(
ndcX * moveAccelerationFactor,
@@ -441,11 +443,11 @@ function interaction(p5, fn){
const uP = this._renderer.states.uPMatrix.mat4;
if (uP[15] === 0) {
- dx = ((uP[8] + cv.x)/uP[0]) * viewZ;
- dy = ((uP[9] + cv.y)/uP[5]) * viewZ;
+ dx = ((uP[8] + cv.x) / uP[0]) * viewZ;
+ dy = ((uP[9] + cv.y) / uP[5]) * viewZ;
} else {
- dx = (cv.x - uP[12])/uP[0];
- dy = (cv.y - uP[13])/uP[5];
+ dx = (cv.x - uP[12]) / uP[0];
+ dy = (cv.y - uP[13]) / uP[5];
}
// translate the camera.
@@ -463,7 +465,6 @@ function interaction(p5, fn){
return this;
};
-
/**
* Adds a grid and an axes icon to clarify orientation in 3D sketches.
*
@@ -667,7 +668,7 @@ function interaction(p5, fn){
* @param {Number} [axesZOff] axes icon offset from the origin along the z-axis.
*/
- fn.debugMode = function(...args) {
+ fn.debugMode = function (...args) {
this._assert3d('debugMode');
// p5._validateParameters('debugMode', args);
@@ -735,7 +736,7 @@ function interaction(p5, fn){
* noDebugMode();
* }
*/
- fn.noDebugMode = function() {
+ fn.noDebugMode = function () {
this._assert3d('noDebugMode');
// start by removing existing 'post' registered debug methods
@@ -760,7 +761,7 @@ function interaction(p5, fn){
* @param {Number} [yOff] offset of grid center from origin in Y axis
* @param {Number} [zOff] offset of grid center from origin in Z axis
*/
- fn._grid = function(size, numDivs, xOff, yOff, zOff) {
+ fn._grid = function (size, numDivs, xOff, yOff, zOff) {
if (typeof size === 'undefined') {
size = this.width / 2;
}
@@ -781,14 +782,17 @@ function interaction(p5, fn){
const spacing = size / numDivs;
const halfSize = size / 2;
- return function() {
+ return function () {
this.push();
this.stroke(
this._renderer.states.curStrokeColor[0] * 255,
this._renderer.states.curStrokeColor[1] * 255,
this._renderer.states.curStrokeColor[2] * 255
);
- this._renderer.states.setValue('uModelMatrix', this._renderer.states.uModelMatrix.clone());
+ this._renderer.states.setValue(
+ 'uModelMatrix',
+ this._renderer.states.uModelMatrix.clone()
+ );
this._renderer.states.uModelMatrix.reset();
// Lines along X axis
@@ -820,7 +824,7 @@ function interaction(p5, fn){
* @param {Number} [yOff] offset of icon from origin in Y axis
* @param {Number} [zOff] offset of icon from origin in Z axis
*/
- fn._axesIcon = function(size, xOff, yOff, zOff) {
+ fn._axesIcon = function (size, xOff, yOff, zOff) {
if (typeof size === 'undefined') {
size = this.width / 20 > 40 ? this.width / 20 : 40;
}
@@ -836,7 +840,10 @@ function interaction(p5, fn){
return () => {
this.push();
- this._renderer.states.setValue('uModelMatrix', this._renderer.states.uModelMatrix.clone());
+ this._renderer.states.setValue(
+ 'uModelMatrix',
+ this._renderer.states.uModelMatrix.clone()
+ );
this._renderer.states.uModelMatrix.reset();
// X axis
@@ -865,6 +872,6 @@ function interaction(p5, fn){
export default interaction;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
interaction(p5, p5.prototype);
}
diff --git a/src/webgl/light.js b/src/webgl/light.js
index 1088c909e1..83e4200c57 100644
--- a/src/webgl/light.js
+++ b/src/webgl/light.js
@@ -8,7 +8,7 @@ import { Renderer3D } from '../core/p5.Renderer3D';
import { Vector } from '../math/p5.Vector';
import { Color } from '../color/p5.Color';
-function light(p5, fn){
+function light(p5, fn) {
/**
* Creates a light that shines from all directions.
*
@@ -1356,11 +1356,12 @@ function light(p5, fn){
return this;
};
-
- Renderer3D.prototype.ambientLight = function(v1, v2, v3, a) {
+ Renderer3D.prototype.ambientLight = function (v1, v2, v3, a) {
const color = this._pInst.color(...arguments);
- this.states.setValue('ambientLightColors', [...this.states.ambientLightColors]);
+ this.states.setValue('ambientLightColors', [
+ ...this.states.ambientLightColors
+ ]);
this.states.ambientLightColors.push(
color._array[0],
color._array[1],
@@ -1370,7 +1371,7 @@ function light(p5, fn){
this.states.setValue('enableLighting', true);
};
- Renderer3D.prototype.specularColor = function(v1, v2, v3) {
+ Renderer3D.prototype.specularColor = function (v1, v2, v3) {
const color = this._pInst.color(...arguments);
this.states.setValue('specularColors', [
@@ -1380,7 +1381,7 @@ function light(p5, fn){
]);
};
- Renderer3D.prototype.directionalLight = function(v1, v2, v3, x, y, z) {
+ Renderer3D.prototype.directionalLight = function (v1, v2, v3, x, y, z) {
let color;
if (v1 instanceof Color) {
color = v1;
@@ -1402,17 +1403,23 @@ function light(p5, fn){
// normalize direction
const l = Math.sqrt(_x * _x + _y * _y + _z * _z);
- this.states.setValue('directionalLightDirections', [...this.states.directionalLightDirections]);
+ this.states.setValue('directionalLightDirections', [
+ ...this.states.directionalLightDirections
+ ]);
this.states.directionalLightDirections.push(_x / l, _y / l, _z / l);
- this.states.setValue('directionalLightDiffuseColors', [...this.states.directionalLightDiffuseColors]);
+ this.states.setValue('directionalLightDiffuseColors', [
+ ...this.states.directionalLightDiffuseColors
+ ]);
this.states.directionalLightDiffuseColors.push(
color._array[0],
color._array[1],
color._array[2]
);
- this.states.setValue('directionalLightSpecularColors', [...this.states.directionalLightSpecularColors]);
+ this.states.setValue('directionalLightSpecularColors', [
+ ...this.states.directionalLightSpecularColors
+ ]);
Array.prototype.push.apply(
this.states.directionalLightSpecularColors,
this.states.specularColors
@@ -1421,7 +1428,7 @@ function light(p5, fn){
this.states.setValue('enableLighting', true);
};
- Renderer3D.prototype.pointLight = function(v1, v2, v3, x, y, z) {
+ Renderer3D.prototype.pointLight = function (v1, v2, v3, x, y, z) {
let color;
if (v1 instanceof Color) {
color = v1;
@@ -1441,17 +1448,23 @@ function light(p5, fn){
_z = v.z;
}
- this.states.setValue('pointLightPositions', [...this.states.pointLightPositions]);
+ this.states.setValue('pointLightPositions', [
+ ...this.states.pointLightPositions
+ ]);
this.states.pointLightPositions.push(_x, _y, _z);
- this.states.setValue('pointLightDiffuseColors', [...this.states.pointLightDiffuseColors]);
+ this.states.setValue('pointLightDiffuseColors', [
+ ...this.states.pointLightDiffuseColors
+ ]);
this.states.pointLightDiffuseColors.push(
color._array[0],
color._array[1],
color._array[2]
);
- this.states.setValue('pointLightSpecularColors', [...this.states.pointLightSpecularColors]);
+ this.states.setValue('pointLightSpecularColors', [
+ ...this.states.pointLightSpecularColors
+ ]);
Array.prototype.push.apply(
this.states.pointLightSpecularColors,
this.states.specularColors
@@ -1460,7 +1473,7 @@ function light(p5, fn){
this.states.setValue('enableLighting', true);
};
- Renderer3D.prototype.imageLight = function(img) {
+ Renderer3D.prototype.imageLight = function (img) {
// activeImageLight property is checked by _setFillUniforms
// for sending uniforms to the fillshader
this.states.setValue('activeImageLight', img);
@@ -1470,13 +1483,13 @@ function light(p5, fn){
this.makeSpecularTexture(img);
};
- Renderer3D.prototype.lights = function() {
+ Renderer3D.prototype.lights = function () {
const grayColor = this._pInst.color('rgb(128,128,128)');
this.ambientLight(grayColor);
this.directionalLight(grayColor, 0, 0, -1);
};
- Renderer3D.prototype.lightFalloff = function(
+ Renderer3D.prototype.lightFalloff = function (
constantAttenuation,
linearAttenuation,
quadraticAttenuation
@@ -1504,7 +1517,8 @@ function light(p5, fn){
if (
constantAttenuation === 0 &&
- (linearAttenuation === 0 && quadraticAttenuation === 0)
+ linearAttenuation === 0 &&
+ quadraticAttenuation === 0
) {
constantAttenuation = 1;
console.warn(
@@ -1517,7 +1531,7 @@ function light(p5, fn){
this.states.setValue('quadraticAttenuation', quadraticAttenuation);
};
- Renderer3D.prototype.spotLight = function(
+ Renderer3D.prototype.spotLight = function (
v1,
v2,
v3,
@@ -1728,13 +1742,19 @@ function light(p5, fn){
}
angle = this._pInst._toRadians(angle);
- this.states.setValue('spotLightAngle', [...this.states.spotLightAngle, Math.cos(angle)]);
- this.states.setValue('spotLightConc', [...this.states.spotLightConc, concentration]);
+ this.states.setValue('spotLightAngle', [
+ ...this.states.spotLightAngle,
+ Math.cos(angle)
+ ]);
+ this.states.setValue('spotLightConc', [
+ ...this.states.spotLightConc,
+ concentration
+ ]);
this.states.setValue('enableLighting', true);
};
- Renderer3D.prototype.noLights = function() {
+ Renderer3D.prototype.noLights = function () {
this.states.setValue('activeImageLight', null);
this.states.setValue('enableLighting', false);
@@ -1766,6 +1786,6 @@ function light(p5, fn){
export default light;
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
light(p5, p5.prototype);
}
diff --git a/src/webgl/loading.js b/src/webgl/loading.js
index 0204bb1a8e..f9a546f03b 100755
--- a/src/webgl/loading.js
+++ b/src/webgl/loading.js
@@ -118,7 +118,8 @@ async function loadMaterialTextures(materials, modelPath, instance) {
if (!material.texturePath) continue;
const url = resolve(material.texturePath);
jobs.push(
- instance.loadImage(url)
+ instance
+ .loadImage(url)
.then(img => {
material.texture = img;
})
@@ -171,7 +172,7 @@ function buildMaterialParts(model, faceMaterials, materials) {
model.parts = parts;
}
-function loading(p5, fn){
+function loading(p5, fn) {
/**
* Loads a 3D model to create a
* p5.Geometry object.
@@ -491,32 +492,32 @@ function loading(p5, fn){
fileType = fileType.fileType || fileType;
flipU = fileType.flipU || false;
flipV = fileType.flipV || false;
-
} else {
// Passing in individual parameters
- if(typeof arguments[arguments.length-1] === 'function'){
- if(typeof arguments[arguments.length-2] === 'function'){
- successCallback = arguments[arguments.length-2];
- failureCallback = arguments[arguments.length-1];
- }else{
- successCallback = arguments[arguments.length-1];
+ if (typeof arguments[arguments.length - 1] === 'function') {
+ if (typeof arguments[arguments.length - 2] === 'function') {
+ successCallback = arguments[arguments.length - 2];
+ failureCallback = arguments[arguments.length - 1];
+ } else {
+ successCallback = arguments[arguments.length - 1];
}
}
if (typeof fileType === 'string') {
- if(typeof normalize !== 'boolean') normalize = false;
-
+ if (typeof normalize !== 'boolean') normalize = false;
} else if (typeof fileType === 'boolean') {
normalize = fileType;
fileType = path.slice(-4);
-
} else {
fileType = path.slice(-4);
normalize = false;
}
}
- if (fileType.toLowerCase() !== '.obj' && fileType.toLowerCase() !== '.stl') {
+ if (
+ fileType.toLowerCase() !== '.obj' &&
+ fileType.toLowerCase() !== '.stl'
+ ) {
fileType = '.obj';
}
@@ -543,18 +544,21 @@ function loading(p5, fn){
}
parsedMaterialPromises.push(
- fileExists(mtlPath).then(exists => {
- if (exists) {
- return parseMtl(mtlPath);
- } else {
- console.warn(`MTL file not found or error in parsing; proceeding without materials: ${mtlPath}`);
+ fileExists(mtlPath)
+ .then(exists => {
+ if (exists) {
+ return parseMtl(mtlPath);
+ } else {
+ console.warn(
+ `MTL file not found or error in parsing; proceeding without materials: ${mtlPath}`
+ );
+ return {};
+ }
+ })
+ .catch(error => {
+ console.warn(`Error loading MTL file: ${mtlPath}`, error);
return {};
-
- }
- }).catch(error => {
- console.warn(`Error loading MTL file: ${mtlPath}`, error);
- return {};
- })
+ })
);
}
}
@@ -568,7 +572,7 @@ function loading(p5, fn){
}
}
- try{
+ try {
if (fileType.match(/\.stl$/i)) {
const { data } = await request(path, 'arrayBuffer');
const cb = () => {
@@ -594,7 +598,6 @@ function loading(p5, fn){
}
};
return this._internal ? this._internal(cb) : cb();
-
} else if (fileType.match(/\.obj$/i)) {
const { data } = await request(path, 'text');
const lines = data.split('\n');
@@ -623,9 +626,9 @@ function loading(p5, fn){
};
return this._internal ? this._internal(cb) : cb();
}
- } catch(err) {
+ } catch (err) {
// p5._friendlyFileLoadError(3, path);
- if(failureCallback) {
+ if (failureCallback) {
return failureCallback(err);
} else {
throw err;
@@ -669,7 +672,6 @@ function loading(p5, fn){
vn: []
};
-
// Map from source index → Map of material → destination index
const usedVerts = {}; // Track colored vertices
let currentMaterial = null;
@@ -728,16 +730,24 @@ function loading(p5, fn){
if (usedVerts[vertString][currentMaterial] === undefined) {
const vertIndex = model.vertices.length;
model.vertices.push(loadedVerts.v.at(vertParts[0]).copy());
- model.uvs.push(loadedVerts.vt.at(vertParts[1]) ?
- loadedVerts.vt.at(vertParts[1]).slice() : [0, 0]);
- model.vertexNormals.push(loadedVerts.vn.at(vertParts[2]) ?
- loadedVerts.vn.at(vertParts[2]).copy() : new Vector(0, 0, 0));
+ model.uvs.push(
+ loadedVerts.vt.at(vertParts[1])
+ ? loadedVerts.vt.at(vertParts[1]).slice()
+ : [0, 0]
+ );
+ model.vertexNormals.push(
+ loadedVerts.vn.at(vertParts[2])
+ ? loadedVerts.vn.at(vertParts[2]).copy()
+ : new Vector(0, 0, 0)
+ );
usedVerts[vertString][currentMaterial] = vertIndex;
face.push(vertIndex);
- if (currentMaterial
- && materials[currentMaterial]
- && materials[currentMaterial].diffuseColor) {
+ if (
+ currentMaterial &&
+ materials[currentMaterial] &&
+ materials[currentMaterial].diffuseColor
+ ) {
hasColoredVertices = true;
const materialDiffuseColor =
materials[currentMaterial].diffuseColor;
@@ -1010,7 +1020,9 @@ function loading(p5, fn){
if (parts[0] !== 'outer' || parts[1] !== 'loop') {
// Invalid State
console.error(line);
- console.error(`Invalid state "${parts[0]}", should be "outer loop"`);
+ console.error(
+ `Invalid state "${parts[0]}", should be "outer loop"`
+ );
return;
} else {
// Next should be vertices
@@ -1071,7 +1083,8 @@ function loading(p5, fn){
// Invalid State
console.error(line);
console.error(
- `Invalid state "${parts[0]
+ `Invalid state "${
+ parts[0]
}", should be "endsolid" or "facet normal"`
);
return;
@@ -1330,9 +1343,9 @@ function loading(p5, fn){
* @return {p5.Geometry} the p5.Geometry object
*/
let modelCounter = 0;
- fn.createModel = function(modelString, fileType=' ', options) {
+ fn.createModel = function (modelString, fileType = ' ', options) {
// p5._validateParameters('createModel', arguments);
- let normalize= false;
+ let normalize = false;
let successCallback;
let failureCallback;
let flipU = false;
@@ -1348,7 +1361,8 @@ function loading(p5, fn){
successCallback = arguments[3];
failureCallback = arguments[4];
} else {
- successCallback = typeof arguments[2] === 'function' ? arguments[2] : undefined;
+ successCallback =
+ typeof arguments[2] === 'function' ? arguments[2] : undefined;
failureCallback = arguments[3];
}
const model = new p5.Geometry();
@@ -1384,7 +1398,8 @@ function loading(p5, fn){
if (failureCallback) {
failureCallback();
} else {
- p5.FES.log`Sorry, the file type is invalid. Only OBJ and STL files are supported.`();
+ p5.FES
+ .log`Sorry, the file type is invalid. Only OBJ and STL files are supported.`();
}
}
if (normalize) {
@@ -1412,6 +1427,6 @@ function loading(p5, fn){
export default loading;
export { parseMtlData, mtlToPartState, buildMaterialParts };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
loading(p5, p5.prototype);
}
diff --git a/src/webgl/material.js b/src/webgl/material.js
index 07f43244eb..5c9f5e8809 100644
--- a/src/webgl/material.js
+++ b/src/webgl/material.js
@@ -4,14 +4,14 @@
* @for p5
*/
-import * as constants from "../core/constants";
-import { Renderer3D } from "../core/p5.Renderer3D";
-import { Shader } from "./p5.Shader";
-import { request } from "../io/files";
-import { Color } from "../color/p5.Color";
+import * as constants from '../core/constants';
+import { Renderer3D } from '../core/p5.Renderer3D';
+import { Shader } from './p5.Shader';
+import { request } from '../io/files';
+import { Color } from '../color/p5.Color';
async function urlToStrandsCallback(url) {
- const src = await fetch(url).then((res) => res.text());
+ const src = await fetch(url).then(res => res.text());
return new Function(src);
}
@@ -130,15 +130,15 @@ function material(p5, fn) {
vertFilename,
fragFilename,
successCallback,
- failureCallback,
+ failureCallback
) {
// p5._validateParameters('loadShader', arguments);
const loadedShader = new Shader();
try {
- loadedShader._vertSrc = (await request(vertFilename, "text")).data;
- loadedShader._fragSrc = (await request(fragFilename, "text")).data;
+ loadedShader._vertSrc = (await request(vertFilename, 'text')).data;
+ loadedShader._fragSrc = (await request(fragFilename, 'text')).data;
if (successCallback) {
return successCallback(loadedShader) || loadedShader;
@@ -513,22 +513,26 @@ function material(p5, fn) {
fn.loadFilterShader = async function (
fragFilename,
successCallback,
- failureCallback,
+ failureCallback
) {
// p5._validateParameters('loadFilterShader', arguments);
try {
// Load the fragment shader
const fragSrc = await this.loadStrings(fragFilename);
- const fragString = await fragSrc.join("\n");
+ const fragString = await fragSrc.join('\n');
// Test if we've loaded GLSL or not by checking for the existence of `void main`
let loadedShader;
if (/void\s+main/.exec(fragString)) {
- loadedShader = this._internal(() => this.createFilterShader(fragString, true));
+ loadedShader = this._internal(() =>
+ this.createFilterShader(fragString, true)
+ );
} else {
- loadedShader = this._internal(() => withGlobalStrands(this, () =>
- this.baseFilterShader().modify(new Function(fragString)),
- ));
+ loadedShader = this._internal(() =>
+ withGlobalStrands(this, () =>
+ this.baseFilterShader().modify(new Function(fragString))
+ )
+ );
}
if (successCallback) {
@@ -871,7 +875,7 @@ function material(p5, fn) {
gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
}
`;
- let vertSrc = fragSrc.includes("#version 300 es")
+ let vertSrc = fragSrc.includes('#version 300 es')
? defaultVertV2
: defaultVertV1;
const shader = new Shader(this._renderer, vertSrc, fragSrc);
@@ -1227,7 +1231,7 @@ function material(p5, fn) {
* }
*/
fn.strokeShader = function (s) {
- this._assert3d("strokeShader");
+ this._assert3d('strokeShader');
// p5._validateParameters('strokeShader', arguments);
this._renderer.strokeShader(s);
@@ -1372,7 +1376,7 @@ function material(p5, fn) {
* }
*/
fn.imageShader = function (s) {
- this._assert3d("imageShader");
+ this._assert3d('imageShader');
// p5._validateParameters('imageShader', arguments);
this._renderer.imageShader(s);
@@ -1630,7 +1634,9 @@ function material(p5, fn) {
fn.loadMaterialShader = async function (url, onSuccess, onFail) {
try {
const cb = await urlToStrandsCallback(url);
- let shader = this._internal(() => withGlobalStrands(this, () => this.buildMaterialShader(cb)));
+ let shader = this._internal(() =>
+ withGlobalStrands(this, () => this.buildMaterialShader(cb))
+ );
if (onSuccess) {
shader = onSuccess(shader) || shader;
}
@@ -1659,7 +1665,7 @@ function material(p5, fn) {
* @returns {p5.Shader} The base material shader.
*/
fn.baseMaterialShader = function () {
- this._assert3d("baseMaterialShader");
+ this._assert3d('baseMaterialShader');
return this._renderer.baseMaterialShader();
};
@@ -1847,9 +1853,9 @@ function material(p5, fn) {
fn.loadNormalShader = async function (url, onSuccess, onFail) {
try {
const cb = await urlToStrandsCallback(url);
- let shader = this._internal(() => this.withGlobalStrands(this, () =>
- this.buildNormalShader(cb),
- ));
+ let shader = this._internal(() =>
+ this.withGlobalStrands(this, () => this.buildNormalShader(cb))
+ );
if (onSuccess) {
shader = onSuccess(shader) || shader;
}
@@ -1879,7 +1885,7 @@ function material(p5, fn) {
* @returns {p5.Shader} The base material shader.
*/
fn.baseNormalShader = function () {
- this._assert3d("baseNormalShader");
+ this._assert3d('baseNormalShader');
return this._renderer.baseNormalShader();
};
@@ -2011,7 +2017,9 @@ function material(p5, fn) {
fn.loadColorShader = async function (url, onSuccess, onFail) {
try {
const cb = await urlToStrandsCallback(url);
- let shader = this._internal(() => withGlobalStrands(this, () => this.buildColorShader(cb)));
+ let shader = this._internal(() =>
+ withGlobalStrands(this, () => this.buildColorShader(cb))
+ );
if (onSuccess) {
shader = onSuccess(shader) || shader;
}
@@ -2040,7 +2048,7 @@ function material(p5, fn) {
* @returns {p5.Shader} The base color shader.
*/
fn.baseColorShader = function () {
- this._assert3d("baseColorShader");
+ this._assert3d('baseColorShader');
return this._renderer.baseColorShader();
};
@@ -2271,7 +2279,9 @@ function material(p5, fn) {
fn.loadStrokeShader = async function (url, onSuccess, onFail) {
try {
const cb = await urlToStrandsCallback(url);
- let shader = this._internal(() => withGlobalStrands(this, () => this.buildStrokeShader(cb)));
+ let shader = this._internal(() =>
+ withGlobalStrands(this, () => this.buildStrokeShader(cb))
+ );
if (onSuccess) {
shader = onSuccess(shader) || shader;
}
@@ -2300,7 +2310,7 @@ function material(p5, fn) {
* @returns {p5.Shader} The base material shader.
*/
fn.baseStrokeShader = function () {
- this._assert3d("baseStrokeShader");
+ this._assert3d('baseStrokeShader');
return this._renderer.baseStrokeShader();
};
@@ -2543,7 +2553,7 @@ function material(p5, fn) {
* }
*/
fn.texture = function (tex) {
- this._assert3d("texture");
+ this._assert3d('texture');
// p5._validateParameters('texture', arguments);
// NOTE: make generic or remove need for
@@ -2721,15 +2731,16 @@ function material(p5, fn) {
* @return {(IMAGE|NORMAL)} The current texture mode, either IMAGE or NORMAL.
*/
fn.textureMode = function (mode) {
- if (typeof mode === 'undefined') { // getter
+ if (typeof mode === 'undefined') {
+ // getter
return this._renderer.states.textureMode;
}
if (mode !== constants.IMAGE && mode !== constants.NORMAL) {
console.warn(
- `You tried to set ${mode} textureMode only supports IMAGE & NORMAL `,
+ `You tried to set ${mode} textureMode only supports IMAGE & NORMAL `
);
} else {
- this._renderer.states.setValue("textureMode", mode);
+ this._renderer.states.setValue('textureMode', mode);
}
};
@@ -2994,7 +3005,8 @@ function material(p5, fn) {
* @return {{x: (CLAMP|REPEAT|MIRROR), y: (CLAMP|REPEAT|MIRROR)}} The current texture wrapping for x and y.
*/
fn.textureWrap = function (wrapX, wrapY = wrapX) {
- if (typeof wrapX === 'undefined') { // getter
+ if (typeof wrapX === 'undefined') {
+ // getter
return {
x: this._renderer.states.textureWrapX,
y: this._renderer.states.textureWrapY
@@ -3051,7 +3063,7 @@ function material(p5, fn) {
* }
*/
fn.normalMaterial = function (...args) {
- this._assert3d("normalMaterial");
+ this._assert3d('normalMaterial');
// p5._validateParameters('normalMaterial', args);
this._renderer.normalMaterial(...args);
@@ -3257,16 +3269,16 @@ function material(p5, fn) {
* @chainable
*/
fn.ambientMaterial = function (v1, v2, v3) {
- this._assert3d("ambientMaterial");
+ this._assert3d('ambientMaterial');
// p5._validateParameters('ambientMaterial', arguments);
const color = fn.color.apply(this, arguments);
- this._renderer.states.setValue("_hasSetAmbient", true);
- this._renderer.states.setValue("curAmbientColor", color._array);
- this._renderer.states.setValue("_useNormalMaterial", false);
- this._renderer.states.setValue("enableLighting", true);
+ this._renderer.states.setValue('_hasSetAmbient', true);
+ this._renderer.states.setValue('curAmbientColor', color._array);
+ this._renderer.states.setValue('_useNormalMaterial', false);
+ this._renderer.states.setValue('enableLighting', true);
if (!this._renderer.states.fillColor) {
- this._renderer.states.setValue("fillColor", new Color([1, 1, 1]));
+ this._renderer.states.setValue('fillColor', new Color([1, 1, 1]));
}
return this;
};
@@ -3349,14 +3361,14 @@ function material(p5, fn) {
* @chainable
*/
fn.emissiveMaterial = function (v1, v2, v3, a) {
- this._assert3d("emissiveMaterial");
+ this._assert3d('emissiveMaterial');
// p5._validateParameters('emissiveMaterial', arguments);
const color = fn.color.apply(this, arguments);
- this._renderer.states.setValue("curEmissiveColor", color._array);
- this._renderer.states.setValue("_useEmissiveMaterial", true);
- this._renderer.states.setValue("_useNormalMaterial", false);
- this._renderer.states.setValue("enableLighting", true);
+ this._renderer.states.setValue('curEmissiveColor', color._array);
+ this._renderer.states.setValue('_useEmissiveMaterial', true);
+ this._renderer.states.setValue('_useNormalMaterial', false);
+ this._renderer.states.setValue('enableLighting', true);
return this;
};
@@ -3589,14 +3601,14 @@ function material(p5, fn) {
* @chainable
*/
fn.specularMaterial = function (v1, v2, v3, alpha) {
- this._assert3d("specularMaterial");
+ this._assert3d('specularMaterial');
// p5._validateParameters('specularMaterial', arguments);
const color = fn.color.apply(this, arguments);
- this._renderer.states.setValue("curSpecularColor", color._array);
- this._renderer.states.setValue("_useSpecularMaterial", true);
- this._renderer.states.setValue("_useNormalMaterial", false);
- this._renderer.states.setValue("enableLighting", true);
+ this._renderer.states.setValue('curSpecularColor', color._array);
+ this._renderer.states.setValue('_useSpecularMaterial', true);
+ this._renderer.states.setValue('_useNormalMaterial', false);
+ this._renderer.states.setValue('enableLighting', true);
return this;
};
@@ -3658,7 +3670,7 @@ function material(p5, fn) {
* }
*/
fn.shininess = function (shine) {
- this._assert3d("shininess");
+ this._assert3d('shininess');
// p5._validateParameters('shininess', arguments);
this._renderer.shininess(shine);
@@ -3767,7 +3779,7 @@ function material(p5, fn) {
* }
*/
fn.metalness = function (metallic) {
- this._assert3d("metalness");
+ this._assert3d('metalness');
this._renderer.metalness(metallic);
@@ -3776,45 +3788,45 @@ function material(p5, fn) {
Renderer3D.prototype.shader = function (s) {
// Always set the shader as a fill shader
- this.states.setValue("userFillShader", s);
- this.states.setValue("_useNormalMaterial", false);
+ this.states.setValue('userFillShader', s);
+ this.states.setValue('_useNormalMaterial', false);
s.ensureCompiledOnContext(this);
s.setDefaultUniforms();
};
Renderer3D.prototype.strokeShader = function (s) {
- this.states.setValue("userStrokeShader", s);
+ this.states.setValue('userStrokeShader', s);
s.ensureCompiledOnContext(this);
s.setDefaultUniforms();
};
Renderer3D.prototype.imageShader = function (s) {
- this.states.setValue("userImageShader", s);
+ this.states.setValue('userImageShader', s);
s.ensureCompiledOnContext(this);
s.setDefaultUniforms();
};
Renderer3D.prototype.resetShader = function () {
- this.states.setValue("userFillShader", null);
- this.states.setValue("userStrokeShader", null);
- this.states.setValue("userImageShader", null);
+ this.states.setValue('userFillShader', null);
+ this.states.setValue('userStrokeShader', null);
+ this.states.setValue('userImageShader', null);
};
Renderer3D.prototype.texture = function (tex) {
- this.states.setValue("drawMode", constants.TEXTURE);
- this.states.setValue("_useNormalMaterial", false);
- this.states.setValue("_tex", tex);
- this.states.setValue("fillColor", new Color([1, 1, 1]));
+ this.states.setValue('drawMode', constants.TEXTURE);
+ this.states.setValue('_useNormalMaterial', false);
+ this.states.setValue('_tex', tex);
+ this.states.setValue('fillColor', new Color([1, 1, 1]));
};
Renderer3D.prototype.normalMaterial = function (...args) {
- this.states.setValue("drawMode", constants.FILL);
- this.states.setValue("_useSpecularMaterial", false);
- this.states.setValue("_useEmissiveMaterial", false);
- this.states.setValue("_useNormalMaterial", true);
- this.states.setValue("curFillColor", [1, 1, 1, 1]);
- this.states.setValue("fillColor", new Color([1, 1, 1]));
- this.states.setValue("strokeColor", null);
+ this.states.setValue('drawMode', constants.FILL);
+ this.states.setValue('_useSpecularMaterial', false);
+ this.states.setValue('_useEmissiveMaterial', false);
+ this.states.setValue('_useNormalMaterial', true);
+ this.states.setValue('curFillColor', [1, 1, 1, 1]);
+ this.states.setValue('fillColor', new Color([1, 1, 1]));
+ this.states.setValue('strokeColor', null);
};
// Renderer3D.prototype.ambientMaterial = function(v1, v2, v3) {
@@ -3830,17 +3842,17 @@ function material(p5, fn) {
if (shine < 1) {
shine = 1;
}
- this.states.setValue("_useShininess", shine);
+ this.states.setValue('_useShininess', shine);
};
Renderer3D.prototype.metalness = function (metallic) {
const metalMix = 1 - Math.exp(-metallic / 100);
- this.states.setValue("_useMetalness", metalMix);
+ this.states.setValue('_useMetalness', metalMix);
};
}
export default material;
-if (typeof p5 !== "undefined") {
+if (typeof p5 !== 'undefined') {
loading(p5, p5.prototype);
}
diff --git a/src/webgl/p5.Camera.js b/src/webgl/p5.Camera.js
index 38929118fe..deff373ac9 100644
--- a/src/webgl/p5.Camera.js
+++ b/src/webgl/p5.Camera.js
@@ -207,14 +207,14 @@ class Camera {
near = 0.01;
console.log(
'Avoid perspective near plane values close to or below 0. ' +
- 'Setting value to 0.01.'
+ 'Setting value to 0.01.'
);
}
if (far < near) {
console.log(
'Perspective far plane value is less than near plane value. ' +
- 'Nothing will be shown.'
+ 'Nothing will be shown.'
);
}
@@ -235,16 +235,33 @@ class Camera {
} else {
// WebGL clip space, z in [-1, 1]
A = (far + near) * nf;
- B = (2 * far * near) * nf;
+ B = 2 * far * near * nf;
}
- this.projMatrix.set(f / aspect, 0, 0, 0,
- 0, -f * this.yScale, 0, 0,
- 0, 0, A, -1,
- 0, 0, B, 0);
+ this.projMatrix.set(
+ f / aspect,
+ 0,
+ 0,
+ 0,
+ 0,
+ -f * this.yScale,
+ 0,
+ 0,
+ 0,
+ 0,
+ A,
+ -1,
+ 0,
+ 0,
+ B,
+ 0
+ );
if (this._isActive()) {
- this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone());
+ this._renderer.states.setValue(
+ 'uPMatrix',
+ this._renderer.states.uPMatrix.clone()
+ );
this._renderer.states.uPMatrix.set(this.projMatrix);
}
}
@@ -412,20 +429,20 @@ class Camera {
const h = top - bottom;
const d = far - near;
const x = +2.0 / w;
- const y = +2.0 / h * this.yScale;
+ const y = (+2.0 / h) * this.yScale;
const z = -2.0 / d;
const tx = -(right + left) / w;
const ty = -(top + bottom) / h;
const tz = -(far + near) / d;
this.projMatrix = new Matrix(4);
- this.projMatrix.set(x, 0, 0, 0,
- 0, -y, 0, 0,
- 0, 0, z, 0,
- tx, ty, tz, 1);
+ this.projMatrix.set(x, 0, 0, 0, 0, -y, 0, 0, 0, 0, z, 0, tx, ty, tz, 1);
if (this._isActive()) {
- this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone());
+ this._renderer.states.setValue(
+ 'uPMatrix',
+ this._renderer.states.uPMatrix.clone()
+ );
this._renderer.states.uPMatrix.set(this.projMatrix);
}
this.cameraType = 'custom';
@@ -543,7 +560,7 @@ class Camera {
const d = far - near;
const x = +(2.0 * near) / w;
- const y = +(2.0 * near) / h * this.yScale;
+ const y = (+(2.0 * near) / h) * this.yScale;
const z = -(2.0 * far * near) / d;
const tx = (right + left) / w;
@@ -552,15 +569,13 @@ class Camera {
this.projMatrix = new Matrix(4);
-
- this.projMatrix.set(x, 0, 0, 0,
- 0, -y, 0, 0,
- tx, ty, tz, -1,
- 0, 0, z, 0);
-
+ this.projMatrix.set(x, 0, 0, 0, 0, -y, 0, 0, tx, ty, tz, -1, 0, 0, z, 0);
if (this._isActive()) {
- this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone());
+ this._renderer.states.setValue(
+ 'uPMatrix',
+ this._renderer.states.uPMatrix.clone()
+ );
this._renderer.states.uPMatrix.set(this.projMatrix);
}
@@ -590,9 +605,15 @@ class Camera {
rotation.rotate4x4(this._renderer._pInst._toRadians(a), x, y, z);
const rotatedCenter = [
- centerX * rotation.mat4[0] + centerY * rotation.mat4[4] + centerZ * rotation.mat4[8],
- centerX * rotation.mat4[1] + centerY * rotation.mat4[5] + centerZ * rotation.mat4[9],
- centerX * rotation.mat4[2] + centerY * rotation.mat4[6] + centerZ * rotation.mat4[10]
+ centerX * rotation.mat4[0] +
+ centerY * rotation.mat4[4] +
+ centerZ * rotation.mat4[8],
+ centerX * rotation.mat4[1] +
+ centerY * rotation.mat4[5] +
+ centerZ * rotation.mat4[9],
+ centerX * rotation.mat4[2] +
+ centerY * rotation.mat4[6] +
+ centerZ * rotation.mat4[10]
];
// add eye position back into center
@@ -673,10 +694,14 @@ class Camera {
const local = this._getLocalAxes();
const axisQuaternion = Quat.fromAxisAngle(
this._renderer._pInst._toRadians(amount),
- local.z[0], local.z[1], local.z[2]);
+ local.z[0],
+ local.z[1],
+ local.z[2]
+ );
// const upQuat = new p5.Quat(0, this.upX, this.upY, this.upZ);
const newUpVector = axisQuaternion.rotateVector(
- new Vector(this.upX, this.upY, this.upZ));
+ new Vector(this.upX, this.upY, this.upZ)
+ );
this.camera(
this.eyeX,
this.eyeY,
@@ -1064,17 +1089,7 @@ class Camera {
* }
* }
*/
- camera(
- eyeX,
- eyeY,
- eyeZ,
- centerX,
- centerY,
- centerZ,
- upX,
- upY,
- upZ
- ) {
+ camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
if (typeof eyeX === 'undefined') {
eyeX = this.defaultEyeX;
eyeY = this.defaultEyeY;
@@ -1109,11 +1124,24 @@ class Camera {
// inverse translates the world to the eye position of the camera
// and rotates it.
- this.cameraMatrix.set(local.x[0], local.y[0], local.z[0], 0,
- local.x[1], local.y[1], local.z[1], 0,
- local.x[2], local.y[2], local.z[2], 0,
- 0, 0, 0, 1);
-
+ this.cameraMatrix.set(
+ local.x[0],
+ local.y[0],
+ local.z[0],
+ 0,
+ local.x[1],
+ local.y[1],
+ local.z[1],
+ 0,
+ local.x[2],
+ local.y[2],
+ local.z[2],
+ 0,
+ 0,
+ 0,
+ 0,
+ 1
+ );
const tx = -eyeX;
const ty = -eyeY;
@@ -1122,7 +1150,10 @@ class Camera {
this.cameraMatrix.translate([tx, ty, tz]);
if (this._isActive()) {
- this._renderer.states.setValue('uViewMatrix', this._renderer.states.uViewMatrix.clone());
+ this._renderer.states.setValue(
+ 'uViewMatrix',
+ this._renderer.states.uViewMatrix.clone()
+ );
this._renderer.states.uViewMatrix.set(this.cameraMatrix);
}
return this;
@@ -1425,11 +1456,22 @@ class Camera {
*/
set(cam) {
const keyNamesOfThePropToCopy = [
- 'eyeX', 'eyeY', 'eyeZ',
- 'centerX', 'centerY', 'centerZ',
- 'upX', 'upY', 'upZ',
- 'cameraFOV', 'aspectRatio', 'cameraNear', 'cameraFar', 'cameraType',
- 'yScale', 'useLinePerspective'
+ 'eyeX',
+ 'eyeY',
+ 'eyeZ',
+ 'centerX',
+ 'centerY',
+ 'centerZ',
+ 'upX',
+ 'upY',
+ 'upZ',
+ 'cameraFOV',
+ 'aspectRatio',
+ 'cameraNear',
+ 'cameraFar',
+ 'cameraType',
+ 'yScale',
+ 'useLinePerspective'
];
for (const keyName of keyNamesOfThePropToCopy) {
this[keyName] = cam[keyName];
@@ -1439,9 +1481,18 @@ class Camera {
this.projMatrix = cam.projMatrix.copy();
if (this._isActive()) {
- this._renderer.states.setValue('uModelMatrix', this._renderer.states.uModelMatrix.clone());
- this._renderer.states.setValue('uViewMatrix', this._renderer.states.uViewMatrix.clone());
- this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone());
+ this._renderer.states.setValue(
+ 'uModelMatrix',
+ this._renderer.states.uModelMatrix.clone()
+ );
+ this._renderer.states.setValue(
+ 'uViewMatrix',
+ this._renderer.states.uViewMatrix.clone()
+ );
+ this._renderer.states.setValue(
+ 'uPMatrix',
+ this._renderer.states.uPMatrix.clone()
+ );
this._renderer.states.uModelMatrix.reset();
this._renderer.states.uViewMatrix.set(this.cameraMatrix);
this._renderer.states.uPMatrix.set(this.projMatrix);
@@ -1534,12 +1585,12 @@ class Camera {
this.projMatrix.setElement(
0,
cam0.projMatrix.mat4[0] *
- Math.pow(cam1.projMatrix.mat4[0] / cam0.projMatrix.mat4[0], amt)
+ Math.pow(cam1.projMatrix.mat4[0] / cam0.projMatrix.mat4[0], amt)
);
this.projMatrix.setElement(
5,
cam0.projMatrix.mat4[5] *
- Math.pow(cam1.projMatrix.mat4[5] / cam0.projMatrix.mat4[5], amt)
+ Math.pow(cam1.projMatrix.mat4[5] / cam0.projMatrix.mat4[5], amt)
);
// If the camera is active, make uPMatrix reflect changes in projMatrix.
if (this._isActive()) {
@@ -1627,16 +1678,28 @@ class Camera {
// calculate newEye, newCenter with newFront vector.
newFront.set(Vector.lerp(front0, front1, amt)).normalize();
- newEye.set(newFront).mult(ratio * lerpedDist).add(lerpedMedium);
- newCenter.set(newFront).mult((ratio - 1) * lerpedDist).add(lerpedMedium);
+ newEye
+ .set(newFront)
+ .mult(ratio * lerpedDist)
+ .add(lerpedMedium);
+ newCenter
+ .set(newFront)
+ .mult((ratio - 1) * lerpedDist)
+ .add(lerpedMedium);
newUp.set(Vector.lerp(up0, up1, amt)).normalize();
// set the camera
this.camera(
- newEye.x, newEye.y, newEye.z,
- newCenter.x, newCenter.y, newCenter.z,
- newUp.x, newUp.y, newUp.z
+ newEye.x,
+ newEye.y,
+ newEye.z,
+ newCenter.x,
+ newCenter.y,
+ newCenter.z,
+ newUp.x,
+ newUp.y,
+ newUp.z
);
return;
}
@@ -1657,21 +1720,19 @@ class Camera {
invOneMinusCosTheta /= a;
b = 0.5 * offDiagSum13 * invOneMinusCosTheta;
c = 0.5 * offDiagSum26 * invOneMinusCosTheta;
- sinTheta = 0.5 * (deltaRot.mat3[7] - deltaRot.mat3[5]) / a;
-
+ sinTheta = (0.5 * (deltaRot.mat3[7] - deltaRot.mat3[5])) / a;
} else if (maxDiag === diag[1]) {
b = Math.sqrt((diag[1] - cosTheta) * invOneMinusCosTheta); // not zero.
invOneMinusCosTheta /= b;
c = 0.5 * offDiagSum57 * invOneMinusCosTheta;
a = 0.5 * offDiagSum13 * invOneMinusCosTheta;
- sinTheta = 0.5 * (deltaRot.mat3[2] - deltaRot.mat3[6]) / b;
-
+ sinTheta = (0.5 * (deltaRot.mat3[2] - deltaRot.mat3[6])) / b;
} else {
c = Math.sqrt((diag[2] - cosTheta) * invOneMinusCosTheta); // not zero.
invOneMinusCosTheta /= c;
a = 0.5 * offDiagSum26 * invOneMinusCosTheta;
b = 0.5 * offDiagSum57 * invOneMinusCosTheta;
- sinTheta = 0.5 * (deltaRot.mat3[3] - deltaRot.mat3[1]) / c;
+ sinTheta = (0.5 * (deltaRot.mat3[3] - deltaRot.mat3[1])) / c;
}
// Constructs a new matrix after interpolating the angles.
@@ -1686,7 +1747,7 @@ class Camera {
const bc = b * c;
const ca = c * a;
// 3x3
- const lerpedRotMat = new Matrix( [
+ const lerpedRotMat = new Matrix([
cosAngle + oneMinusCosAngle * a * a,
oneMinusCosAngle * ab + sinAngle * c,
oneMinusCosAngle * ca - sinAngle * b,
@@ -1702,17 +1763,29 @@ class Camera {
// calculate newEye, newCenter with newFront vector.
lerpedRotMat.multiplyVec(front0, newFront); // this is vec3
- newEye.set(newFront).mult(ratio * lerpedDist).add(lerpedMedium);
- newCenter.set(newFront).mult((ratio - 1) * lerpedDist).add(lerpedMedium);
+ newEye
+ .set(newFront)
+ .mult(ratio * lerpedDist)
+ .add(lerpedMedium);
+ newCenter
+ .set(newFront)
+ .mult((ratio - 1) * lerpedDist)
+ .add(lerpedMedium);
lerpedRotMat.multiplyVec(up0, newUp); // this is vec3
// We also get the up vector in the same way and set the camera.
// The eye position and center position are calculated based on the front vector.
this.camera(
- newEye.x, newEye.y, newEye.z,
- newCenter.x, newCenter.y, newCenter.z,
- newUp.x, newUp.y, newUp.z
+ newEye.x,
+ newEye.y,
+ newEye.z,
+ newCenter.x,
+ newCenter.y,
+ newCenter.z,
+ newUp.x,
+ newUp.y,
+ newUp.z
);
}
@@ -1732,7 +1805,8 @@ class Camera {
this.defaultCenterX = 0;
this.defaultCenterY = 0;
this.defaultCenterZ = 0;
- this.defaultCameraNear = this.defaultEyeZ * this._renderer.defaultNearScale();
+ this.defaultCameraNear =
+ this.defaultEyeZ * this._renderer.defaultNearScale();
this.defaultCameraFar = this.defaultEyeZ * this._renderer.defaultFarScale();
}
@@ -1935,9 +2009,15 @@ class Camera {
// update camera
this.camera(
- this.eyeX, this.eyeY, this.eyeZ,
- this.centerX, this.centerY, this.centerZ,
- this.upX, this.upY, this.upZ
+ this.eyeX,
+ this.eyeY,
+ this.eyeZ,
+ this.centerX,
+ this.centerY,
+ this.centerZ,
+ this.upX,
+ this.upY,
+ this.upZ
);
}
@@ -2013,8 +2093,12 @@ class Camera {
front.x + this.centerX,
front.y + this.centerY,
front.z + this.centerZ,
- this.centerX, this.centerY, this.centerZ,
- up.x, up.y, up.z
+ this.centerX,
+ this.centerY,
+ this.centerZ,
+ up.x,
+ up.y,
+ up.z
);
}
@@ -2025,9 +2109,9 @@ class Camera {
_isActive() {
return this === this._renderer.states.curCamera;
}
-};
+}
-function camera(p5, fn){
+function camera(p5, fn) {
////////////////////////////////////////////////////////////////////////////////
// p5.Prototype Methods
////////////////////////////////////////////////////////////////////////////////
@@ -2397,7 +2481,6 @@ function camera(p5, fn){
return this._renderer.linePerspective(enable);
};
-
/**
* Sets an orthographic projection for the current camera in a 3D sketch.
*
@@ -2855,17 +2938,17 @@ function camera(p5, fn){
*/
p5.Camera = Camera;
- Renderer3D.prototype.camera = function(...args) {
+ Renderer3D.prototype.camera = function (...args) {
this.states.setValue('curCamera', this.states.curCamera.clone());
this.states.curCamera.camera(...args);
};
- Renderer3D.prototype.perspective = function(...args) {
+ Renderer3D.prototype.perspective = function (...args) {
this.states.setValue('curCamera', this.states.curCamera.clone());
this.states.curCamera.perspective(...args);
};
- Renderer3D.prototype.linePerspective = function(enable) {
+ Renderer3D.prototype.linePerspective = function (enable) {
if (enable !== undefined) {
this.states.setValue('curCamera', this.states.curCamera.clone());
// Set the line perspective if enable is provided
@@ -2876,17 +2959,17 @@ function camera(p5, fn){
}
};
- Renderer3D.prototype.ortho = function(...args) {
+ Renderer3D.prototype.ortho = function (...args) {
this.states.setValue('curCamera', this.states.curCamera.clone());
this.states.curCamera.ortho(...args);
};
- Renderer3D.prototype.frustum = function(...args) {
+ Renderer3D.prototype.frustum = function (...args) {
this.states.setValue('curCamera', this.states.curCamera.clone());
this.states.curCamera.frustum(...args);
};
- Renderer3D.prototype.createCamera = function() {
+ Renderer3D.prototype.createCamera = function () {
// compute default camera settings, then set a default camera
const _cam = new Camera(this);
_cam._computeCameraDefaultSettings();
@@ -2895,7 +2978,7 @@ function camera(p5, fn){
return _cam;
};
- Renderer3D.prototype.setCamera = function(cam) {
+ Renderer3D.prototype.setCamera = function (cam) {
this.states.setValue('curCamera', cam);
// set the projection matrix (which is not normally updated each frame)
@@ -3857,6 +3940,6 @@ function camera(p5, fn){
export default camera;
export { Camera };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
camera(p5, p5.prototype);
}
diff --git a/src/webgl/p5.DataArray.js b/src/webgl/p5.DataArray.js
index 9f5db5c33e..43e1068927 100644
--- a/src/webgl/p5.DataArray.js
+++ b/src/webgl/p5.DataArray.js
@@ -78,9 +78,9 @@ class DataArray {
this.data = newData;
}
}
-};
+}
-function dataArray(p5, fn){
+function dataArray(p5, fn) {
/**
* An internal class to store data that will be sent to a p5.RenderBuffer.
* Those need to eventually go into a Float32Array, so this class provides a
@@ -108,6 +108,6 @@ function dataArray(p5, fn){
export default dataArray;
export { DataArray };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
dataArray(p5, p5.prototype);
}
diff --git a/src/webgl/p5.Framebuffer.js b/src/webgl/p5.Framebuffer.js
index c29e184545..9c63a1b419 100644
--- a/src/webgl/p5.Framebuffer.js
+++ b/src/webgl/p5.Framebuffer.js
@@ -68,11 +68,9 @@ class Framebuffer {
this.pixels = [];
this.format = settings.format || constants.UNSIGNED_BYTE;
- this.channels = settings.channels || (
- this.renderer.defaultFramebufferAlpha()
- ? RGBA
- : RGB
- );
+ this.channels =
+ settings.channels ||
+ (this.renderer.defaultFramebufferAlpha() ? RGBA : RGB);
this.useDepth = settings.depth === undefined ? true : settings.depth;
this.depthFormat = settings.depthFormat || constants.FLOAT;
this.textureFiltering = settings.textureFiltering || constants.LINEAR;
@@ -92,8 +90,11 @@ class Framebuffer {
}
this.density = settings.density || this.renderer._pixelDensity;
if (settings.width && settings.height) {
- const dimensions =
- this.renderer._adjustDimensions(settings.width, settings.height, this.density);
+ const dimensions = this.renderer._adjustDimensions(
+ settings.width,
+ settings.height,
+ this.density
+ );
this.width = dimensions.adjustedWidth;
this.height = dimensions.adjustedHeight;
this._autoSized = false;
@@ -113,9 +114,12 @@ class Framebuffer {
this.renderer.validateFramebufferFormats(this);
if (settings.stencil && !this.useDepth) {
- console.warn('A stencil buffer can only be used if also using depth. Since the framebuffer has no depth buffer, the stencil buffer will be ignored.');
+ console.warn(
+ 'A stencil buffer can only be used if also using depth. Since the framebuffer has no depth buffer, the stencil buffer will be ignored.'
+ );
}
- this.useStencil = this.useDepth &&
+ this.useStencil =
+ this.useDepth &&
(settings.stencil === undefined ? true : settings.stencil);
// Let renderer create framebuffer resources with antialiasing support
@@ -174,8 +178,11 @@ class Framebuffer {
*/
resize(width, height) {
this._autoSized = false;
- const dimensions =
- this.renderer._adjustDimensions(width, height, this.density);
+ const dimensions = this.renderer._adjustDimensions(
+ width,
+ height,
+ this.density
+ );
width = dimensions.adjustedWidth;
height = dimensions.adjustedHeight;
this.width = width;
@@ -377,11 +384,13 @@ class Framebuffer {
this.depthFormat = constants.UNSIGNED_INT;
}
- if (![
- constants.UNSIGNED_BYTE,
- constants.FLOAT,
- constants.HALF_FLOAT
- ].includes(this.format)) {
+ if (
+ ![
+ constants.UNSIGNED_BYTE,
+ constants.FLOAT,
+ constants.HALF_FLOAT
+ ].includes(this.format)
+ ) {
console.warn(
'Unknown Framebuffer format. ' +
'Please use UNSIGNED_BYTE, FLOAT, or HALF_FLOAT. ' +
@@ -389,10 +398,10 @@ class Framebuffer {
);
this.format = constants.UNSIGNED_BYTE;
}
- if (this.useDepth && ![
- constants.UNSIGNED_INT,
- constants.FLOAT
- ].includes(this.depthFormat)) {
+ if (
+ this.useDepth &&
+ ![constants.UNSIGNED_INT, constants.FLOAT].includes(this.depthFormat)
+ ) {
console.warn(
'Unknown Framebuffer depth format. ' +
'Please use UNSIGNED_INT or FLOAT. Defaulting to FLOAT.'
@@ -458,29 +467,22 @@ class Framebuffer {
if (this.useDepth) {
this.depth = new FramebufferTexture(this, 'depthTexture');
const depthFilter = constants.NEAREST;
- this.depthP5Texture = new Texture(
- this.renderer,
- this.depth,
- {
- minFilter: depthFilter,
- magFilter: depthFilter
- }
- );
+ this.depthP5Texture = new Texture(this.renderer, this.depth, {
+ minFilter: depthFilter,
+ magFilter: depthFilter
+ });
this.renderer.textures.set(this.depth, this.depthP5Texture);
}
this.color = new FramebufferTexture(this, 'colorTexture');
- const filter = this.textureFiltering === constants.LINEAR
- ? constants.LINEAR
- : constants.NEAREST;
- this.colorP5Texture = new Texture(
- this.renderer,
- this.color,
- {
- minFilter: filter,
- magFilter: filter
- }
- );
+ const filter =
+ this.textureFiltering === constants.LINEAR
+ ? constants.LINEAR
+ : constants.NEAREST;
+ this.colorP5Texture = new Texture(this.renderer, this.color, {
+ minFilter: filter,
+ magFilter: filter
+ });
this.renderer.textures.set(this.color, this.colorP5Texture);
}
@@ -774,14 +776,21 @@ class Framebuffer {
// this.renderer.setCamera(this.defaultCamera);
this.renderer.states.setValue('curCamera', this.defaultCamera);
// set the projection matrix (which is not normally updated each frame)
- this.renderer.states.setValue('uPMatrix', this.renderer.states.uPMatrix.clone());
+ this.renderer.states.setValue(
+ 'uPMatrix',
+ this.renderer.states.uPMatrix.clone()
+ );
this.renderer.states.uPMatrix.set(this.defaultCamera.projMatrix);
- this.renderer.states.setValue('uViewMatrix', this.renderer.states.uViewMatrix.clone());
+ this.renderer.states.setValue(
+ 'uViewMatrix',
+ this.renderer.states.uViewMatrix.clone()
+ );
this.renderer.states.uViewMatrix.set(this.defaultCamera.cameraMatrix);
this.renderer.resetMatrix();
- this.renderer.states.uViewMatrix
- .set(this.renderer.states.curCamera.cameraMatrix);
+ this.renderer.states.uViewMatrix.set(
+ this.renderer.states.curCamera.cameraMatrix
+ );
this.renderer.states.uModelMatrix.reset();
this.renderer._applyStencilTestIfClipping();
}
@@ -892,7 +901,9 @@ class Framebuffer {
const fbo = this.renderer.activeFramebuffers.pop();
if (fbo !== this) {
- throw new Error("It looks like you've called end() while another Framebuffer is active.");
+ throw new Error(
+ "It looks like you've called end() while another Framebuffer is active."
+ );
}
this._beforeEnd();
if (this.prevFramebuffer) {
@@ -1076,7 +1087,11 @@ class Framebuffer {
y = constrain(y, 0, this.height - 1);
}
- return this.renderer.readFramebufferPixel(this, x * this.density, y * this.density);
+ return this.renderer.readFramebufferPixel(
+ this,
+ x * this.density,
+ y * this.density
+ );
}
x = constrain(x, 0, this.width - 1);
@@ -1137,7 +1152,7 @@ class Framebuffer {
}
}
-function framebuffer(p5, fn){
+function framebuffer(p5, fn) {
/**
* A p5.Camera attached to a
* p5.Framebuffer.
@@ -1405,6 +1420,6 @@ function framebuffer(p5, fn){
export default framebuffer;
export { FramebufferTexture, FramebufferCamera, Framebuffer };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
framebuffer(p5, p5.prototype);
}
diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js
index 646aa5afec..cd842eaa67 100644
--- a/src/webgl/p5.Geometry.js
+++ b/src/webgl/p5.Geometry.js
@@ -19,7 +19,6 @@ class Geometry {
this.boundingBoxCache = null;
-
//an array containing every vertex for stroke drawing
this.lineVertices = new DataArray();
@@ -190,9 +189,15 @@ class Geometry {
}
let minVertex = new Vector(
- Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+ Number.MAX_VALUE,
+ Number.MAX_VALUE,
+ Number.MAX_VALUE
+ );
let maxVertex = new Vector(
- Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
+ Number.MIN_VALUE,
+ Number.MIN_VALUE,
+ Number.MIN_VALUE
+ );
for (let i = 0; i < this.vertices.length; i++) {
let vertex = this.vertices[i];
@@ -205,10 +210,16 @@ class Geometry {
maxVertex.z = Math.max(maxVertex.z, vertex.z);
}
// Calculate size and offset properties
- let size = new Vector(maxVertex.x - minVertex.x,
- maxVertex.y - minVertex.y, maxVertex.z - minVertex.z);
- let offset = new Vector((minVertex.x + maxVertex.x) / 2,
- (minVertex.y + maxVertex.y) / 2, (minVertex.z + maxVertex.z) / 2);
+ let size = new Vector(
+ maxVertex.x - minVertex.x,
+ maxVertex.y - minVertex.y,
+ maxVertex.z - minVertex.z
+ );
+ let offset = new Vector(
+ (minVertex.x + maxVertex.x) / 2,
+ (minVertex.y + maxVertex.y) / 2,
+ (minVertex.z + maxVertex.z) / 2
+ );
// Cache the result for future access
this.boundingBoxCache = {
@@ -241,7 +252,7 @@ class Geometry {
this.vertexNormals.length = 0;
this.uvs.length = 0;
- for (const propName in this.userVertexProperties){
+ for (const propName in this.userVertexProperties) {
this.userVertexProperties[propName].delete();
}
this.userVertexProperties = {};
@@ -379,8 +390,7 @@ class Geometry {
* }
*/
saveObj(fileName = 'model.obj') {
- let objStr= '';
-
+ let objStr = '';
// Vertices
this.vertices.forEach(v => {
@@ -399,7 +409,6 @@ class Geometry {
this.vertexNormals.forEach(n => {
objStr += `vn ${n.x} ${n.y} ${n.z}\n`;
});
-
}
// Faces, obj vertex indices begin with 1 and not 0
// texture coordinate (uvs) and vertexNormal indices
@@ -407,7 +416,7 @@ class Geometry {
// ex 1/1/1 or 2//2 for vertices without uvs
this.faces.forEach(face => {
let faceStr = 'f';
- face.forEach(index =>{
+ face.forEach(index => {
faceStr += ' ';
faceStr += index + 1;
if (this.vertexNormals.length > 0 || this.uvs.length > 0) {
@@ -425,8 +434,7 @@ class Geometry {
});
const blob = new Blob([objStr], { type: 'text/plain' });
- downloadFile(blob, fileName , 'obj');
-
+ downloadFile(blob, fileName, 'obj');
}
/**
@@ -485,7 +493,7 @@ class Geometry {
* model(myModel);
* }
*/
- saveStl(fileName = 'model.stl', { binary = false } = {}){
+ saveStl(fileName = 'model.stl', { binary = false } = {}) {
let modelOutput;
let name = fileName.substring(0, fileName.lastIndexOf('.'));
let faceNormals = [];
@@ -1396,30 +1404,30 @@ class Geometry {
const isPoint = currEdge[0] === currEdge[1];
const begin = this.vertices[currEdge[0]];
const end = this.vertices[currEdge[1]];
- const prevColor = (this.vertexStrokeColors.length > 0 && prevEdge)
- ? this.vertexStrokeColors.slice(
- prevEdge[1] * 4,
- (prevEdge[1] + 1) * 4
- )
- : [0, 0, 0, 0];
- const fromColor = this.vertexStrokeColors.length > 0
- ? this.vertexStrokeColors.slice(
- currEdge[0] * 4,
- (currEdge[0] + 1) * 4
- )
- : [0, 0, 0, 0];
- const toColor = this.vertexStrokeColors.length > 0
- ? this.vertexStrokeColors.slice(
- currEdge[1] * 4,
- (currEdge[1] + 1) * 4
- )
- : [0, 0, 0, 0];
+ const prevColor =
+ this.vertexStrokeColors.length > 0 && prevEdge
+ ? this.vertexStrokeColors.slice(
+ prevEdge[1] * 4,
+ (prevEdge[1] + 1) * 4
+ )
+ : [0, 0, 0, 0];
+ const fromColor =
+ this.vertexStrokeColors.length > 0
+ ? this.vertexStrokeColors.slice(
+ currEdge[0] * 4,
+ (currEdge[0] + 1) * 4
+ )
+ : [0, 0, 0, 0];
+ const toColor =
+ this.vertexStrokeColors.length > 0
+ ? this.vertexStrokeColors.slice(
+ currEdge[1] * 4,
+ (currEdge[1] + 1) * 4
+ )
+ : [0, 0, 0, 0];
const dir = isPoint
? new Vector(0, 1, 0)
- : end
- .copy()
- .sub(begin)
- .normalize();
+ : end.copy().sub(begin).normalize();
const dirOK = dir.magSq() > 0;
if (dirOK) {
this._addSegment(begin, end, fromColor, toColor, dir);
@@ -1447,12 +1455,7 @@ class Geometry {
if (dirOK && !connected.has(currEdge[0])) {
const existingCap = potentialCaps.get(currEdge[0]);
if (existingCap) {
- this._addJoin(
- begin,
- existingCap.dir,
- dir,
- fromColor
- );
+ this._addJoin(begin, existingCap.dir, dir, fromColor);
potentialCaps.delete(currEdge[0]);
connected.add(currEdge[0]);
} else {
@@ -1489,12 +1492,7 @@ class Geometry {
if (i === this.edges.length - 1 && !connected.has(currEdge[1])) {
const existingCap = potentialCaps.get(currEdge[1]);
if (existingCap) {
- this._addJoin(
- end,
- dir,
- existingCap.dir.copy().mult(-1),
- toColor
- );
+ this._addJoin(end, dir, existingCap.dir.copy().mult(-1), toColor);
potentialCaps.delete(currEdge[1]);
connected.add(currEdge[1]);
} else {
@@ -1533,13 +1531,7 @@ class Geometry {
* @private
* @chainable
*/
- _addSegment(
- begin,
- end,
- fromColor,
- toColor,
- dir
- ) {
+ _addSegment(begin, end, fromColor, toColor, dir) {
const a = begin.array();
const b = end.array();
const dirArr = dir.array();
@@ -1616,12 +1608,7 @@ class Geometry {
* @private
* @chainable
*/
- _addJoin(
- point,
- fromTangent,
- toTangent,
- color
- ) {
+ _addJoin(point, fromTangent, toTangent, color) {
const ptArray = point.array();
const tanInArray = fromTangent.array();
const tanOutArray = toTangent.array();
@@ -1811,32 +1798,32 @@ class Geometry {
* @param {Number|Number[]} data the data tied to the vertex property.
* @param {Number} [size] optional size of each unit of data.
*/
- vertexProperty(propertyName, data, size){
+ vertexProperty(propertyName, data, size) {
let prop;
- if (!this.userVertexProperties[propertyName]){
+ if (!this.userVertexProperties[propertyName]) {
prop = this.userVertexProperties[propertyName] =
this._userVertexPropertyHelper(propertyName, data, size);
}
prop = this.userVertexProperties[propertyName];
- if (size){
+ if (size) {
prop.pushDirect(data);
- } else{
+ } else {
prop.setCurrentData(data);
prop.pushCurrentData();
}
}
- _userVertexPropertyHelper(propertyName, data, size){
+ _userVertexPropertyHelper(propertyName, data, size) {
const geometryInstance = this;
- const prop = this.userVertexProperties[propertyName] = {
+ const prop = (this.userVertexProperties[propertyName] = {
name: propertyName,
dataSize: size ? size : data.length ? data.length : 1,
geometry: geometryInstance,
// Getters
- getName(){
+ getName() {
return this.name;
},
- getCurrentData(){
+ getCurrentData() {
if (this.currentData === undefined) {
this.currentData = new Array(this.getDataSize()).fill(0);
}
@@ -1866,18 +1853,18 @@ class Geometry {
this.currentData = data;
},
// Utilities
- pushCurrentData(){
+ pushCurrentData() {
const data = this.getCurrentData();
this.pushDirect(data);
},
pushDirect(data) {
- if (data.length){
+ if (data.length) {
this.getSrcArray().push(...data);
- } else{
+ } else {
this.getSrcArray().push(data);
}
},
- resetSrcArray(){
+ resetSrcArray() {
this.geometry[this.getSrcName()] = [];
},
delete() {
@@ -1885,11 +1872,11 @@ class Geometry {
delete this.geometry[srcName];
delete this;
}
- };
+ });
this[prop.getSrcName()] = [];
return this.userVertexProperties[propertyName];
}
-};
+}
/**
* Keeps track of how many custom geometry objects have been made so that each
@@ -1897,7 +1884,7 @@ class Geometry {
*/
Geometry.nextId = 0;
-function geometry(p5, fn){
+function geometry(p5, fn) {
/**
* A class to describe a 3D shape.
*
@@ -2503,6 +2490,6 @@ function geometry(p5, fn){
export default geometry;
export { Geometry };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
geometry(p5, p5.prototype);
}
diff --git a/src/webgl/p5.GeometryPart.js b/src/webgl/p5.GeometryPart.js
index 45664bf37e..7afa6e152a 100644
--- a/src/webgl/p5.GeometryPart.js
+++ b/src/webgl/p5.GeometryPart.js
@@ -9,11 +9,11 @@
// channel is 0..1 (same range as the renderer's curFillColor), not 0..255.
function createPartState() {
return {
- fill: null, // Kd + d -> [r, g, b, a] | null, each 0..1
- ambientColor: null, // Ka -> [r, g, b] | null, each 0..1
- specularColor: null, // Ks -> [r, g, b] | null, each 0..1
- shininess: null, // Ns -> number | null
- texture: null // map_Kd -> p5.Image | null
+ fill: null, // Kd + d -> [r, g, b, a] | null, each 0..1
+ ambientColor: null, // Ka -> [r, g, b] | null, each 0..1
+ specularColor: null, // Ks -> [r, g, b] | null, each 0..1
+ shininess: null, // Ns -> number | null
+ texture: null // map_Kd -> p5.Image | null
};
}
diff --git a/src/webgl/p5.Quat.js b/src/webgl/p5.Quat.js
index d9204aec32..4391d3d8d5 100644
--- a/src/webgl/p5.Quat.js
+++ b/src/webgl/p5.Quat.js
@@ -23,8 +23,8 @@ class Quat {
* @chainable
*/
static fromAxisAngle(angle, x, y, z) {
- const w = Math.cos(angle/2);
- const vec = new Vector(x, y, z).normalize().mult(Math.sin(angle/2));
+ const w = Math.cos(angle / 2);
+ const vec = new Vector(x, y, z).normalize().mult(Math.sin(angle / 2));
return new Quat(w, vec.x, vec.y, vec.z);
}
@@ -39,14 +39,24 @@ class Quat {
* @chainable
*/
multiply(quat) {
-
return new Quat(
- this.w * quat.w - this.vec.x * quat.vec.x - this.vec.y * quat.vec.y - this.vec.z * quat.vec.z,
- this.w * quat.vec.x + this.vec.x * quat.w + this.vec.y * quat.vec.z - this.vec.z * quat.vec.y,
- this.w * quat.vec.y - this.vec.x * quat.vec.z + this.vec.y * quat.w + this.vec.z * quat.vec.x,
- this.w * quat.vec.z + this.vec.x * quat.vec.y - this.vec.y * quat.vec.x + this.vec.z * quat.w
+ this.w * quat.w -
+ this.vec.x * quat.vec.x -
+ this.vec.y * quat.vec.y -
+ this.vec.z * quat.vec.z,
+ this.w * quat.vec.x +
+ this.vec.x * quat.w +
+ this.vec.y * quat.vec.z -
+ this.vec.z * quat.vec.y,
+ this.w * quat.vec.y -
+ this.vec.x * quat.vec.z +
+ this.vec.y * quat.w +
+ this.vec.z * quat.vec.x,
+ this.w * quat.vec.z +
+ this.vec.x * quat.vec.y -
+ this.vec.y * quat.vec.x +
+ this.vec.z * quat.w
);
-
}
/**
@@ -59,9 +69,9 @@ class Quat {
* @param {p5.Vector} [p] vector to rotate on the axis quaternion
*/
rotateVector(p) {
- return Vector.mult( p, this.w*this.w - this.vec.dot(this.vec) )
- .add( Vector.mult( this.vec, 2 * p.dot(this.vec) ) )
- .add( Vector.mult( this.vec, 2 * this.w ).cross( p ) )
+ return Vector.mult(p, this.w * this.w - this.vec.dot(this.vec))
+ .add(Vector.mult(this.vec, 2 * p.dot(this.vec)))
+ .add(Vector.mult(this.vec, 2 * this.w).cross(p))
.clampToZero();
}
@@ -75,12 +85,14 @@ class Quat {
* @chainable
*/
rotateBy(axesQuat) {
- return axesQuat.multiply(this).multiply(axesQuat.conjugate()).
- vec.clampToZero();
+ return axesQuat
+ .multiply(this)
+ .multiply(axesQuat.conjugate())
+ .vec.clampToZero();
}
}
-function quat(p5, fn){
+function quat(p5, fn) {
/**
* A class to describe a Quaternion
* for vector rotations in the p5js webgl renderer.
@@ -100,6 +112,6 @@ function quat(p5, fn){
export default quat;
export { Quat };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
quat(p5, p5.prototype);
}
diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js
index 91a766dc6f..dbdbe3c201 100644
--- a/src/webgl/p5.RendererGL.js
+++ b/src/webgl/p5.RendererGL.js
@@ -20,24 +20,24 @@ import { glslBackend } from './strands_glslBackend';
import { TypeInfoFromGLSLName } from '../strands/ir_types.js';
import { getShaderHookTypes } from './shaderHookUtils';
-import filterBaseVert from "./shaders/filters/base.vert";
-import lightingShader from "./shaders/lighting.glsl";
-import webgl2CompatibilityShader from "./shaders/webgl2Compatibility.glsl";
-import normalVert from "./shaders/normal.vert";
-import normalFrag from "./shaders/normal.frag";
-import basicFrag from "./shaders/basic.frag";
-import lightVert from "./shaders/light.vert";
-import lightTextureFrag from "./shaders/light_texture.frag";
-import phongVert from "./shaders/phong.vert";
-import phongFrag from "./shaders/phong.frag";
-import fontVert from "./shaders/font.vert";
-import fontFrag from "./shaders/font.frag";
-import lineVert from "./shaders/line.vert";
-import lineFrag from "./shaders/line.frag";
-import imageLightVert from "./shaders/imageLight.vert";
-import imageLightDiffusedFrag from "./shaders/imageLightDiffused.frag";
-import imageLightSpecularFrag from "./shaders/imageLightSpecular.frag";
-import filterBaseFrag from "./shaders/filters/base.frag";
+import filterBaseVert from './shaders/filters/base.vert';
+import lightingShader from './shaders/lighting.glsl';
+import webgl2CompatibilityShader from './shaders/webgl2Compatibility.glsl';
+import normalVert from './shaders/normal.vert';
+import normalFrag from './shaders/normal.frag';
+import basicFrag from './shaders/basic.frag';
+import lightVert from './shaders/light.vert';
+import lightTextureFrag from './shaders/light_texture.frag';
+import phongVert from './shaders/phong.vert';
+import phongFrag from './shaders/phong.frag';
+import fontVert from './shaders/font.vert';
+import fontFrag from './shaders/font.frag';
+import lineVert from './shaders/line.vert';
+import lineFrag from './shaders/line.frag';
+import imageLightVert from './shaders/imageLight.vert';
+import imageLightDiffusedFrag from './shaders/imageLightDiffused.frag';
+import imageLightSpecularFrag from './shaders/imageLightSpecular.frag';
+import filterBaseFrag from './shaders/filters/base.frag';
const { lineDefs } = getStrokeDefs((n, v) => `#define ${n} ${v}\n`);
@@ -57,7 +57,7 @@ const defaultShaders = {
imageLightDiffusedFrag,
imageLightSpecularFrag,
filterBaseVert,
- filterBaseFrag,
+ filterBaseFrag
};
for (const key in defaultShaders) {
defaultShaders[key] = webgl2CompatibilityShader + defaultShaders[key];
@@ -78,7 +78,7 @@ class RendererGL extends Renderer3D {
if (this.webglVersion === constants.WEBGL2) {
this.blendExt = this.GL;
} else {
- this.blendExt = this.GL.getExtension("EXT_blend_minmax");
+ this.blendExt = this.GL.getExtension('EXT_blend_minmax');
}
this._userEnabledStencil = false;
@@ -87,7 +87,7 @@ class RendererGL extends Renderer3D {
this._internalDisable = this.drawingContext.disable;
// Override WebGL enable function
- this.drawingContext.enable = (key) => {
+ this.drawingContext.enable = key => {
if (key === this.drawingContext.STENCIL_TEST) {
if (!this._clipping) {
this._userEnabledStencil = true;
@@ -97,9 +97,9 @@ class RendererGL extends Renderer3D {
};
// Override WebGL disable function
- this.drawingContext.disable = (key) => {
+ this.drawingContext.disable = key => {
if (key === this.drawingContext.STENCIL_TEST) {
- this._userEnabledStencil = false;
+ this._userEnabledStencil = false;
}
return this._internalDisable.call(this.drawingContext, key);
};
@@ -125,7 +125,7 @@ class RendererGL extends Renderer3D {
* @param {Number[]} color [description]
* @return {Number[]} Normalized numbers array
*/
- _applyBlendMode () {
+ _applyBlendMode() {
if (this._cachedBlendMode === this.states.curBlendMode) {
return;
}
@@ -207,7 +207,6 @@ class RendererGL extends Renderer3D {
return undefined;
}
-
/**
* Restores the original shader variable names in a given text using
* the provided shader name map.
@@ -270,23 +269,23 @@ class RendererGL extends Renderer3D {
if (!glBuffers) return;
- if (this._curShader.shaderType === 'stroke'){
+ if (this._curShader.shaderType === 'stroke') {
if (count === 1) {
gl.drawArrays(gl.TRIANGLES, 0, geometry.lineVertices.length / 3);
- } else {
- try {
+ } else {
+ try {
gl.drawArraysInstanced(
- gl.TRIANGLES,
+ gl.TRIANGLES,
0,
geometry.lineVertices.length / 3,
count
);
} catch (e) {
console.log(
- "🌸 p5.js says: Instancing is only supported in WebGL2 mode"
+ '🌸 p5.js says: Instancing is only supported in WebGL2 mode'
);
}
- }
+ }
} else if (this._curShader.shaderType === 'text') {
// Text rendering uses a fixed quad geometry with 6 indices
this._bindBuffer(glBuffers.indexBuffer, gl.ELEMENT_ARRAY_BUFFER);
@@ -300,9 +299,9 @@ class RendererGL extends Renderer3D {
this._pInst.webglVersion !== constants.WEBGL2 &&
glBuffers.indexBufferType === gl.UNSIGNED_INT
) {
- if (!gl.getExtension("OES_element_index_uint")) {
+ if (!gl.getExtension('OES_element_index_uint')) {
throw new Error(
- "Unable to render a 3d model with > 65535 triangles. Your web browser does not support the WebGL Extension OES_element_index_uint."
+ 'Unable to render a 3d model with > 65535 triangles. Your web browser does not support the WebGL Extension OES_element_index_uint.'
);
}
}
@@ -325,7 +324,7 @@ class RendererGL extends Renderer3D {
);
} catch (e) {
console.log(
- "🌸 p5.js says: Instancing is only supported in WebGL2 mode"
+ '🌸 p5.js says: Instancing is only supported in WebGL2 mode'
);
}
}
@@ -345,7 +344,7 @@ class RendererGL extends Renderer3D {
gl.drawArraysInstanced(glMode, 0, geometry.vertices.length, count);
} catch (e) {
console.log(
- "🌸 p5.js says: Instancing is only supported in WebGL2 mode"
+ '🌸 p5.js says: Instancing is only supported in WebGL2 mode'
);
}
}
@@ -369,7 +368,7 @@ class RendererGL extends Renderer3D {
_setAttributeDefaults(pInst) {
// See issue #3850, safer to enable AA in Safari
- const applyAA = navigator.userAgent.toLowerCase().includes("safari");
+ const applyAA = navigator.userAgent.toLowerCase().includes('safari');
const defaults = {
alpha: true,
depth: true,
@@ -378,7 +377,7 @@ class RendererGL extends Renderer3D {
premultipliedAlpha: true,
preserveDrawingBuffer: true,
perPixelLighting: true,
- version: 2,
+ version: 2
};
if (pInst._glAttributes === null) {
pInst._glAttributes = defaults;
@@ -389,15 +388,15 @@ class RendererGL extends Renderer3D {
}
_setAttributes(key, value) {
- if (typeof this._pInst._glAttributes === "undefined") {
+ if (typeof this._pInst._glAttributes === 'undefined') {
console.log(
- "You are trying to use setAttributes on a p5.Graphics object " +
- "that does not use a WEBGL renderer."
+ 'You are trying to use setAttributes on a p5.Graphics object ' +
+ 'that does not use a WEBGL renderer.'
);
return;
}
let unchanged = true;
- if (typeof value !== "undefined") {
+ if (typeof value !== 'undefined') {
//first time modifying the attributes
if (this._pInst._glAttributes === null) {
this._pInst._glAttributes = {};
@@ -421,7 +420,8 @@ class RendererGL extends Renderer3D {
if (!this._pInst._setupDone) {
if (this.geometryBufferCache.numCached() > 0) {
- p5.FES.log`Sorry, Could not set the attributes, you need to call setAttributes() before calling the other drawing methods in setup()`();
+ p5.FES
+ .log`Sorry, Could not set the attributes, you need to call setAttributes() before calling the other drawing methods in setup()`();
return;
}
}
@@ -437,7 +437,7 @@ class RendererGL extends Renderer3D {
if (this._pInst._glAttributes?.version !== 1) {
// Unless WebGL1 is explicitly asked for, try to create a WebGL2 context
this.drawingContext = this.canvas.getContext(
- "webgl2",
+ 'webgl2',
this._pInst._glAttributes
);
}
@@ -451,11 +451,11 @@ class RendererGL extends Renderer3D {
// disabled via `setAttributes({ version: 1 })` or because the device
// doesn't support it), fall back to a WebGL1 context
this.drawingContext =
- this.canvas.getContext("webgl", this._pInst._glAttributes) ||
- this.canvas.getContext("experimental-webgl", this._pInst._glAttributes);
+ this.canvas.getContext('webgl', this._pInst._glAttributes) ||
+ this.canvas.getContext('experimental-webgl', this._pInst._glAttributes);
}
if (this.drawingContext === null) {
- throw new Error("Error creating webgl context");
+ throw new Error('Error creating webgl context');
} else {
const gl = this.drawingContext;
gl.enable(gl.DEPTH_TEST);
@@ -484,15 +484,13 @@ class RendererGL extends Renderer3D {
}
let maxTextureSize = this._maxTextureSize;
- let maxAllowedPixelDimensions = Math.floor(
- maxTextureSize / density
- );
+ let maxAllowedPixelDimensions = Math.floor(maxTextureSize / density);
let adjustedWidth = Math.min(width, maxAllowedPixelDimensions);
let adjustedHeight = Math.min(height, maxAllowedPixelDimensions);
if (adjustedWidth !== width || adjustedHeight !== height) {
console.warn(
- "Warning: The requested width/height exceeds hardware limits. " +
+ 'Warning: The requested width/height exceeds hardware limits. ' +
`Adjusting dimensions to width: ${adjustedWidth}, height: ${adjustedHeight}.`
);
}
@@ -575,8 +573,8 @@ class RendererGL extends Renderer3D {
//@todo_FES
if (this._pInst._glAttributes.preserveDrawingBuffer !== true) {
console.log(
- "loadPixels only works in WebGL when preserveDrawingBuffer " +
- "is true."
+ 'loadPixels only works in WebGL when preserveDrawingBuffer ' +
+ 'is true.'
);
return;
}
@@ -605,7 +603,7 @@ class RendererGL extends Renderer3D {
this.push();
this.resetMatrix();
this.clear();
- this.states.setValue("imageMode", constants.CORNER);
+ this.states.setValue('imageMode', constants.CORNER);
this.image(
fbo,
0,
@@ -644,7 +642,7 @@ class RendererGL extends Renderer3D {
_updateViewport() {
this._origViewport = {
width: this.GL.drawingBufferWidth,
- height: this.GL.drawingBufferHeight,
+ height: this.GL.drawingBufferHeight
};
this.viewport(this._origViewport.width, this._origViewport.height);
}
@@ -714,7 +712,6 @@ class RendererGL extends Renderer3D {
}
}
-
//////////////////////////////////////////////
// SHADER
//////////////////////////////////////////////
@@ -728,7 +725,7 @@ class RendererGL extends Renderer3D {
baseMaterialShader() {
if (!this._pInst._glAttributes.perPixelLighting) {
throw new Error(
- "The material shader does not support hooks without perPixelLighting. Try turning it back on."
+ 'The material shader does not support hooks without perPixelLighting. Try turning it back on.'
);
}
return super.baseMaterialShader();
@@ -739,22 +736,22 @@ class RendererGL extends Renderer3D {
if (this._pInst._glAttributes.perPixelLighting) {
this._defaultLightShader = new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.phongVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.phongFrag,
{
vertex: {
- "void beforeVertex": "() {}",
- "Vertex getObjectInputs": "(Vertex inputs) { return inputs; }",
- "Vertex getWorldInputs": "(Vertex inputs) { return inputs; }",
- "Vertex getCameraInputs": "(Vertex inputs) { return inputs; }",
- "void afterVertex": "() {}",
+ 'void beforeVertex': '() {}',
+ 'Vertex getObjectInputs': '(Vertex inputs) { return inputs; }',
+ 'Vertex getWorldInputs': '(Vertex inputs) { return inputs; }',
+ 'Vertex getCameraInputs': '(Vertex inputs) { return inputs; }',
+ 'void afterVertex': '() {}'
},
fragment: {
- "void beforeFragment": "() {}",
- "Inputs getPixelInputs": "(Inputs inputs) { return inputs; }",
- "vec4 combineColors": `(ColorComponents components) {
+ 'void beforeFragment': '() {}',
+ 'Inputs getPixelInputs': '(Inputs inputs) { return inputs; }',
+ 'vec4 combineColors': `(ColorComponents components) {
vec4 color = vec4(0.);
color.rgb += components.diffuse * components.baseColor;
color.rgb += components.ambient * components.ambientColor;
@@ -763,17 +760,18 @@ class RendererGL extends Renderer3D {
color.a = components.opacity;
return color;
}`,
- "vec4 getFinalColor": "(vec4 color, vec2 texCoord) { return color; }",
- "void afterFragment": "() {}",
- },
+ 'vec4 getFinalColor':
+ '(vec4 color, vec2 texCoord) { return color; }',
+ 'void afterFragment': '() {}'
+ }
}
);
} else {
this._defaultLightShader = new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.lightVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.lightTextureFrag
);
}
@@ -786,23 +784,24 @@ class RendererGL extends Renderer3D {
if (!this._defaultNormalShader) {
this._defaultNormalShader = new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.normalVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.normalFrag,
{
vertex: {
- "void beforeVertex": "() {}",
- "Vertex getObjectInputs": "(Vertex inputs) { return inputs; }",
- "Vertex getWorldInputs": "(Vertex inputs) { return inputs; }",
- "Vertex getCameraInputs": "(Vertex inputs) { return inputs; }",
- "void afterVertex": "() {}",
+ 'void beforeVertex': '() {}',
+ 'Vertex getObjectInputs': '(Vertex inputs) { return inputs; }',
+ 'Vertex getWorldInputs': '(Vertex inputs) { return inputs; }',
+ 'Vertex getCameraInputs': '(Vertex inputs) { return inputs; }',
+ 'void afterVertex': '() {}'
},
fragment: {
- "void beforeFragment": "() {}",
- "vec4 getFinalColor": "(vec4 color, vec2 texCoord) { return color; }",
- "void afterFragment": "() {}",
- },
+ 'void beforeFragment': '() {}',
+ 'vec4 getFinalColor':
+ '(vec4 color, vec2 texCoord) { return color; }',
+ 'void afterFragment': '() {}'
+ }
}
);
}
@@ -814,23 +813,24 @@ class RendererGL extends Renderer3D {
if (!this._defaultColorShader) {
this._defaultColorShader = new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.normalVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.basicFrag,
{
vertex: {
- "void beforeVertex": "() {}",
- "Vertex getObjectInputs": "(Vertex inputs) { return inputs; }",
- "Vertex getWorldInputs": "(Vertex inputs) { return inputs; }",
- "Vertex getCameraInputs": "(Vertex inputs) { return inputs; }",
- "void afterVertex": "() {}",
+ 'void beforeVertex': '() {}',
+ 'Vertex getObjectInputs': '(Vertex inputs) { return inputs; }',
+ 'Vertex getWorldInputs': '(Vertex inputs) { return inputs; }',
+ 'Vertex getCameraInputs': '(Vertex inputs) { return inputs; }',
+ 'void afterVertex': '() {}'
},
fragment: {
- "void beforeFragment": "() {}",
- "vec4 getFinalColor": "(vec4 color, vec2 texCoord) { return color; }",
- "void afterFragment": "() {}",
- },
+ 'void beforeFragment': '() {}',
+ 'vec4 getFinalColor':
+ '(vec4 color, vec2 texCoord) { return color; }',
+ 'void afterFragment': '() {}'
+ }
}
);
}
@@ -842,28 +842,29 @@ class RendererGL extends Renderer3D {
if (!this._defaultLineShader) {
this._defaultLineShader = new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.lineVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.lineFrag,
{
vertex: {
- "void beforeVertex": "() {}",
- "StrokeVertex getObjectInputs":
- "(StrokeVertex inputs) { return inputs; }",
- "StrokeVertex getWorldInputs":
- "(StrokeVertex inputs) { return inputs; }",
- "StrokeVertex getCameraInputs":
- "(StrokeVertex inputs) { return inputs; }",
- "void afterVertex": "() {}",
+ 'void beforeVertex': '() {}',
+ 'StrokeVertex getObjectInputs':
+ '(StrokeVertex inputs) { return inputs; }',
+ 'StrokeVertex getWorldInputs':
+ '(StrokeVertex inputs) { return inputs; }',
+ 'StrokeVertex getCameraInputs':
+ '(StrokeVertex inputs) { return inputs; }',
+ 'void afterVertex': '() {}'
},
fragment: {
- "void beforeFragment": "() {}",
- "Inputs getPixelInputs": "(Inputs inputs) { return inputs; }",
- "vec4 getFinalColor": "(vec4 color, vec2 texCoord) { return color; }",
- "bool shouldDiscard": "(bool outside) { return outside; }",
- "void afterFragment": "() {}",
- },
+ 'void beforeFragment': '() {}',
+ 'Inputs getPixelInputs': '(Inputs inputs) { return inputs; }',
+ 'vec4 getFinalColor':
+ '(vec4 color, vec2 texCoord) { return color; }',
+ 'bool shouldDiscard': '(bool outside) { return outside; }',
+ 'void afterFragment': '() {}'
+ }
}
);
}
@@ -874,13 +875,13 @@ class RendererGL extends Renderer3D {
_getFontShader() {
if (!this._defaultFontShader) {
if (this.webglVersion === constants.WEBGL) {
- this.GL.getExtension("OES_standard_derivatives");
+ this.GL.getExtension('OES_standard_derivatives');
}
this._defaultFontShader = new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.fontVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.fontFrag
);
}
@@ -891,20 +892,20 @@ class RendererGL extends Renderer3D {
if (!this._baseFilterShader) {
this._baseFilterShader = new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.filterBaseVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.filterBaseFrag,
{
vertex: {},
fragment: {
- "vec4 getColor": `(FilterInputs inputs, in sampler2D canvasContent) {
+ 'vec4 getColor': `(FilterInputs inputs, in sampler2D canvasContent) {
return getTexture(canvasContent, inputs.texCoord);
- }`,
+ }`
},
hookAliases: {
- 'getColor': ['filterColor'],
- },
+ getColor: ['filterColor']
+ }
}
);
}
@@ -912,14 +913,14 @@ class RendererGL extends Renderer3D {
}
_webGL2CompatibilityPrefix(shaderType, floatPrecision) {
- let code = "";
+ let code = '';
if (this.webglVersion === constants.WEBGL2) {
- code += "#version 300 es\n#define WEBGL2\n";
+ code += '#version 300 es\n#define WEBGL2\n';
}
- if (shaderType === "vert") {
- code += "#define VERTEX_SHADER\n";
- } else if (shaderType === "frag") {
- code += "#define FRAGMENT_SHADER\n";
+ if (shaderType === 'vert') {
+ code += '#define VERTEX_SHADER\n';
+ } else if (shaderType === 'frag') {
+ code += '#define FRAGMENT_SHADER\n';
}
if (floatPrecision) {
code += `precision ${floatPrecision} float;\n`;
@@ -934,24 +935,23 @@ class RendererGL extends Renderer3D {
if (type === 'diffused') {
return new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.imageLightVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.imageLightDiffusedFrag
);
} else if (type === 'specular') {
return new Shader(
this,
- this._webGL2CompatibilityPrefix("vert", "highp") +
+ this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.imageLightVert,
- this._webGL2CompatibilityPrefix("frag", "highp") +
+ this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.imageLightSpecularFrag
);
}
throw new Error(`Unknown imageLight shader type: ${type}`);
}
-
/*
* WebGL-specific implementation of mipmap texture creation
*/
@@ -970,7 +970,9 @@ class RendererGL extends Renderer3D {
* Accumulate ImageData from framebuffer for WebGL
*/
_accumulateMipLevel(framebuffer, mipmapData, mipLevel, width, height) {
- const imageData = framebuffer.get().drawingContext.getImageData(0, 0, width, height);
+ const imageData = framebuffer
+ .get()
+ .drawingContext.getImageData(0, 0, width, height);
mipmapData.levels.push(imageData);
}
@@ -980,7 +982,7 @@ class RendererGL extends Renderer3D {
_finalizeMipmapTexture(mipmapData) {
return new MipmapTexture(this, mipmapData.levels, {
minFilter: constants.LINEAR_MIPMAP,
- magFilter: constants.LINEAR,
+ magFilter: constants.LINEAR
});
}
@@ -1007,7 +1009,11 @@ class RendererGL extends Renderer3D {
// Set mipmap-appropriate filtering
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
+ gl.texParameteri(
+ gl.TEXTURE_2D,
+ gl.TEXTURE_MIN_FILTER,
+ gl.LINEAR_MIPMAP_LINEAR
+ );
gl.bindTexture(gl.TEXTURE_2D, null);
@@ -1108,7 +1114,8 @@ class RendererGL extends Renderer3D {
// If we're using a Uint32Array for our indexBuffer we will need to pass a
// different enum value to WebGL draw triangles. This happens in
// the _drawElements function.
- buffers.indexBufferType = indexType === Uint32Array ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;
+ buffers.indexBufferType =
+ indexType === Uint32Array ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;
} else if (buffers.indexBuffer) {
// the index buffer is unused, remove it
gl.deleteBuffer(buffers.indexBuffer);
@@ -1154,9 +1161,11 @@ class RendererGL extends Renderer3D {
shader.vertSrc(),
shaderNameMap
);
- throw new Error(`Yikes! An error occurred compiling the vertex shader: ${
- vertexInfoLog
- } in:\n\n${vertexSource}`);
+ throw new Error(
+ `Yikes! An error occurred compiling the vertex shader: ${
+ vertexInfoLog
+ } in:\n\n${vertexSource}`
+ );
}
const fragShader = gl.createShader(gl.FRAGMENT_SHADER);
@@ -1172,9 +1181,11 @@ class RendererGL extends Renderer3D {
shader.fragSrc(),
shaderNameMap
);
- throw new Error(`Darn! An error occurred compiling the fragment shader: ${
- fragmentInfoLog
- } in:\n\n${fragmentSource}`);
+ throw new Error(
+ `Darn! An error occurred compiling the fragment shader: ${
+ fragmentInfoLog
+ } in:\n\n${fragmentSource}`
+ );
}
const program = gl.createProgram();
@@ -1209,7 +1220,7 @@ class RendererGL extends Renderer3D {
shader,
uniform,
data,
- (tex) => this.getTexture(tex),
+ tex => this.getTexture(tex),
this.GL
);
}
@@ -1248,8 +1259,17 @@ class RendererGL extends Renderer3D {
const gl = this.GL;
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
- gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.texImage2D(
+ gl.TEXTURE_2D,
+ 0,
+ gl.RGBA,
+ width,
+ height,
+ 0,
+ gl.RGBA,
+ gl.UNSIGNED_BYTE,
+ null
+ );
// TODO use format and data type
return { texture: tex, glFormat: gl.RGBA, glDataType: gl.UNSIGNED_BYTE };
}
@@ -1264,7 +1284,12 @@ class RendererGL extends Renderer3D {
gl.texImage2D(gl.TEXTURE_2D, 0, glFormat, glFormat, glDataType, source);
}
- uploadTextureFromData({ texture, glFormat, glDataType }, data, width, height) {
+ uploadTextureFromData(
+ { texture, glFormat, glDataType },
+ data,
+ width,
+ height
+ ) {
const gl = this.GL;
gl.texImage2D(
gl.TEXTURE_2D,
@@ -1299,7 +1324,6 @@ class RendererGL extends Renderer3D {
this.GL.deleteTexture(texture);
}
-
/**
* @private blends colors according to color components.
* If alpha value is less than 1, or non-standard blendMode
@@ -1414,11 +1438,13 @@ class RendererGL extends Renderer3D {
framebuffer.depthFormat = constants.UNSIGNED_INT;
}
- if (![
- constants.UNSIGNED_BYTE,
- constants.FLOAT,
- constants.HALF_FLOAT
- ].includes(framebuffer.format)) {
+ if (
+ ![
+ constants.UNSIGNED_BYTE,
+ constants.FLOAT,
+ constants.HALF_FLOAT
+ ].includes(framebuffer.format)
+ ) {
console.warn(
'Unknown Framebuffer format. ' +
'Please use UNSIGNED_BYTE, FLOAT, or HALF_FLOAT. ' +
@@ -1426,10 +1452,12 @@ class RendererGL extends Renderer3D {
);
framebuffer.format = constants.UNSIGNED_BYTE;
}
- if (framebuffer.useDepth && ![
- constants.UNSIGNED_INT,
- constants.FLOAT
- ].includes(framebuffer.depthFormat)) {
+ if (
+ framebuffer.useDepth &&
+ ![constants.UNSIGNED_INT, constants.FLOAT].includes(
+ framebuffer.depthFormat
+ )
+ ) {
console.warn(
'Unknown Framebuffer depth format. ' +
'Please use UNSIGNED_INT or FLOAT. Defaulting to FLOAT.'
@@ -1531,7 +1559,9 @@ class RendererGL extends Renderer3D {
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
- framebuffer.useStencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT,
+ framebuffer.useStencil
+ ? gl.DEPTH_STENCIL_ATTACHMENT
+ : gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D,
depthTexture,
0
@@ -1547,7 +1577,10 @@ class RendererGL extends Renderer3D {
gl.RENDERBUFFER,
Math.max(
0,
- Math.min(framebuffer.antialiasSamples, gl.getParameter(gl.MAX_SAMPLES))
+ Math.min(
+ framebuffer.antialiasSamples,
+ gl.getParameter(gl.MAX_SAMPLES)
+ )
),
colorFormat.internalFormat,
framebuffer.width * framebuffer.density,
@@ -1562,7 +1595,10 @@ class RendererGL extends Renderer3D {
gl.RENDERBUFFER,
Math.max(
0,
- Math.min(framebuffer.antialiasSamples, gl.getParameter(gl.MAX_SAMPLES))
+ Math.min(
+ framebuffer.antialiasSamples,
+ gl.getParameter(gl.MAX_SAMPLES)
+ )
),
depthFormat.internalFormat,
framebuffer.width * framebuffer.density,
@@ -1580,7 +1616,9 @@ class RendererGL extends Renderer3D {
if (framebuffer.useDepth) {
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER,
- framebuffer.useStencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT,
+ framebuffer.useStencil
+ ? gl.DEPTH_STENCIL_ATTACHMENT
+ : gl.DEPTH_ATTACHMENT,
gl.RENDERBUFFER,
framebuffer.depthRenderbuffer
);
@@ -1613,9 +1651,10 @@ class RendererGL extends Renderer3D {
if (framebuffer.format === constants.FLOAT) {
type = gl.FLOAT;
} else if (framebuffer.format === constants.HALF_FLOAT) {
- type = this.webglVersion === constants.WEBGL2
- ? gl.HALF_FLOAT
- : gl.getExtension('OES_texture_half_float').HALF_FLOAT_OES;
+ type =
+ this.webglVersion === constants.WEBGL2
+ ? gl.HALF_FLOAT
+ : gl.getExtension('OES_texture_half_float').HALF_FLOAT_OES;
} else {
type = gl.UNSIGNED_BYTE;
}
@@ -1720,11 +1759,13 @@ class RendererGL extends Renderer3D {
}
deleteFramebufferTextures(framebuffer) {
- this._deleteFramebufferTexture(framebuffer.color)
+ this._deleteFramebufferTexture(framebuffer.color);
if (framebuffer.depth) this._deleteFramebufferTexture(framebuffer.depth);
const gl = this.GL;
- if (framebuffer.colorRenderbuffer) gl.deleteRenderbuffer(framebuffer.colorRenderbuffer);
- if (framebuffer.depthRenderbuffer) gl.deleteRenderbuffer(framebuffer.depthRenderbuffer);
+ if (framebuffer.colorRenderbuffer)
+ gl.deleteRenderbuffer(framebuffer.colorRenderbuffer);
+ if (framebuffer.depthRenderbuffer)
+ gl.deleteRenderbuffer(framebuffer.depthRenderbuffer);
}
deleteFramebufferResources(framebuffer) {
@@ -1757,13 +1798,17 @@ class RendererGL extends Renderer3D {
const partsToCopy = {
colorTexture: [
gl.COLOR_BUFFER_BIT,
- framebuffer.colorP5Texture.magFilter === constants.LINEAR ? gl.LINEAR : gl.NEAREST
- ],
+ framebuffer.colorP5Texture.magFilter === constants.LINEAR
+ ? gl.LINEAR
+ : gl.NEAREST
+ ]
};
if (framebuffer.useDepth) {
partsToCopy.depthTexture = [
gl.DEPTH_BUFFER_BIT,
- framebuffer.depthP5Texture.magFilter === constants.LINEAR ? gl.LINEAR : gl.NEAREST
+ framebuffer.depthP5Texture.magFilter === constants.LINEAR
+ ? gl.LINEAR
+ : gl.NEAREST
];
}
const [flag, filter] = partsToCopy[property];
@@ -1789,9 +1834,7 @@ class RendererGL extends Renderer3D {
const gl = this.GL;
gl.bindFramebuffer(
gl.FRAMEBUFFER,
- framebuffer
- ? this.getFramebufferToBind(framebuffer)
- : null
+ framebuffer ? this.getFramebufferToBind(framebuffer) : null
);
}
@@ -1870,9 +1913,10 @@ class RendererGL extends Renderer3D {
if (channel < channels) {
// Find the index of this pixel in `rawData`, which might have a
// different number of channels
- const rawDataIdx = channels === 4
- ? idx
- : (yPos * w * framebuffer.density + xPos) * channels + channel;
+ const rawDataIdx =
+ channels === 4
+ ? idx
+ : (yPos * w * framebuffer.density + xPos) * channels + channel;
fullData[idx] = rawData[rawDataIdx];
}
}
@@ -1881,10 +1925,9 @@ class RendererGL extends Renderer3D {
// Create image from data
const region = new Image(w * framebuffer.density, h * framebuffer.density);
- region.imageData = region.canvas.getContext('2d').createImageData(
- region.width,
- region.height
- );
+ region.imageData = region.canvas
+ .getContext('2d')
+ .createImageData(region.width, region.height);
region.imageData.data.set(fullData);
region.pixels = region.imageData.data;
region.updatePixels();
@@ -1900,10 +1943,19 @@ class RendererGL extends Renderer3D {
const colorFormat = this._getFramebufferColorFormat(framebuffer);
const channels = colorFormat.format === gl.RGBA ? 4 : 3;
- const len = framebuffer.width * framebuffer.height * framebuffer.density * framebuffer.density * channels;
- const TypedArrayClass = colorFormat.type === gl.UNSIGNED_BYTE ? Uint8Array : Float32Array;
+ const len =
+ framebuffer.width *
+ framebuffer.height *
+ framebuffer.density *
+ framebuffer.density *
+ channels;
+ const TypedArrayClass =
+ colorFormat.type === gl.UNSIGNED_BYTE ? Uint8Array : Float32Array;
- if (!(framebuffer.pixels instanceof TypedArrayClass) || framebuffer.pixels.length !== len) {
+ if (
+ !(framebuffer.pixels instanceof TypedArrayClass) ||
+ framebuffer.pixels.length !== len
+ ) {
throw new Error(
'The pixels array has not been set correctly. Please call loadPixels() before updatePixels().'
);
@@ -1942,10 +1994,14 @@ class RendererGL extends Renderer3D {
this._drawingFilter = true;
this.image(
framebuffer,
- 0, 0,
- framebuffer.width, framebuffer.height,
- -this.width / 2, -this.height / 2,
- this.width, this.height
+ 0,
+ 0,
+ framebuffer.width,
+ framebuffer.height,
+ -this.width / 2,
+ -this.height / 2,
+ this.width,
+ this.height
);
this._drawingFilter = false;
this.pop();
@@ -1963,7 +2019,6 @@ class RendererGL extends Renderer3D {
this.bindFramebuffer(prevFramebuffer);
}
}
-
}
function rendererGL(p5, fn) {
@@ -2133,6 +2188,6 @@ function rendererGL(p5, fn) {
export default rendererGL;
export { RendererGL };
-if (typeof p5 !== "undefined") {
+if (typeof p5 !== 'undefined') {
rendererGL(p5, p5.prototype);
}
diff --git a/src/webgl/p5.Shader.js b/src/webgl/p5.Shader.js
index 4af976807c..68dbba20bf 100644
--- a/src/webgl/p5.Shader.js
+++ b/src/webgl/p5.Shader.js
@@ -12,9 +12,9 @@ class Shader {
// Detect compute shader: first arg is STRING and second is undefined OR an options object
if (
- typeof vertSrc === 'string' && (
- fragSrc === undefined || (typeof fragSrc === 'object' && !Array.isArray(fragSrc))
- )
+ typeof vertSrc === 'string' &&
+ (fragSrc === undefined ||
+ (typeof fragSrc === 'object' && !Array.isArray(fragSrc)))
) {
// Compute shader
this.shaderType = 'compute';
@@ -87,7 +87,7 @@ class Shader {
modified: {
vertex: (options.modified && options.modified.vertex) || {},
fragment: (options.modified && options.modified.fragment) || {},
- compute: (options.modified && options.modified.compute) || {},
+ compute: (options.modified && options.modified.compute) || {}
}
};
}
@@ -483,7 +483,11 @@ class Shader {
declarations:
(this.hooks.declarations || '') + '\n' + (hooks.declarations || ''),
uniforms: Object.assign({}, this.hooks.uniforms, hooks.uniforms || {}),
- storageUniforms: Object.assign({}, this.hooks.storageUniforms, hooks.storageUniforms || {}),
+ storageUniforms: Object.assign(
+ {},
+ this.hooks.storageUniforms,
+ hooks.storageUniforms || {}
+ ),
// Preserve shader name remapping and suffix state across chained modify()
// calls so later passes keep earlier internal names and keep incrementing.
shaderNameMap: {
@@ -504,17 +508,24 @@ class Shader {
this.hooks.shaderNameState?.nextSuffix ??
0
},
- varyingVariables: (hooks.varyingVariables || []).concat(this.hooks.varyingVariables || []),
- instanceIDVarying: hooks.instanceIDVarying || this.hooks.instanceIDVarying || null,
+ varyingVariables: (hooks.varyingVariables || []).concat(
+ this.hooks.varyingVariables || []
+ ),
+ instanceIDVarying:
+ hooks.instanceIDVarying || this.hooks.instanceIDVarying || null,
fragment: Object.assign({}, this.hooks.fragment, newHooks.fragment || {}),
vertex: Object.assign({}, this.hooks.vertex, newHooks.vertex || {}),
compute: Object.assign({}, this.hooks.compute, newHooks.compute || {}),
helpers: Object.assign({}, this.hooks.helpers, newHooks.helpers || {}),
- hookAliases: Object.assign({}, this.hooks.hookAliases, newHooks.hookAliases || {}),
+ hookAliases: Object.assign(
+ {},
+ this.hooks.hookAliases,
+ newHooks.hookAliases || {}
+ ),
modified: {
vertex: modifiedVertex,
fragment: modifiedFragment,
- compute: modifiedCompute,
+ compute: modifiedCompute
}
});
@@ -572,7 +583,8 @@ class Shader {
}
for (const name in this.hooks.storageUniforms) {
const initializer = this.hooks.storageUniforms[name];
- const value = initializer instanceof Function ? initializer() : initializer;
+ const value =
+ initializer instanceof Function ? initializer() : initializer;
if (value !== undefined && value !== null) {
this.setUniform(name, value);
}
@@ -809,7 +821,7 @@ class Shader {
for (const meta of uniformMetadata) {
const uniform = {
...meta,
- _cachedData: undefined,
+ _cachedData: undefined
};
if (uniform.isSampler) {
@@ -863,14 +875,12 @@ class Shader {
let tex = uniform.texture;
if (
tex === undefined ||
- (
- // Make sure we unbind a framebuffer uniform if it's the same
- // framebuffer that is actvely being drawn to in order to
- // prevent a feedback cycle
- tex.isFramebufferTexture &&
+ // Make sure we unbind a framebuffer uniform if it's the same
+ // framebuffer that is actvely being drawn to in order to
+ // prevent a feedback cycle
+ (tex.isFramebufferTexture &&
!tex.src.framebuffer.antialias &&
- tex.src.framebuffer === this._renderer.activeFramebuffer()
- )
+ tex.src.framebuffer === this._renderer.activeFramebuffer())
) {
// user hasn't yet supplied a texture for this slot.
// (or there may not be one--maybe just lighting),
@@ -1148,7 +1158,10 @@ class Shader {
// duck typing instead of instanceof to avoid accidentally importing
// and comparing against a separate copy of p5 classes
if (data?.isVector) {
- data = data.values.length !== data.dimensions ? data.values.slice(0, data.dimensions) : data.values;
+ data =
+ data.values.length !== data.dimensions
+ ? data.values.slice(0, data.dimensions)
+ : data.values;
} else if (data?.isColor) {
data = data._getRGBA([1, 1, 1, 1]);
}
@@ -1166,7 +1179,10 @@ class Shader {
return;
} else {
if (Array.isArray(data) || data instanceof TypedArray) {
- if (uniform._cachedData && this._renderer._arraysEqual(uniform._cachedData, data)) {
+ if (
+ uniform._cachedData &&
+ this._renderer._arraysEqual(uniform._cachedData, data)
+ ) {
return;
}
uniform._cachedData = data.slice(0);
@@ -1195,14 +1211,22 @@ class Shader {
}
if (attr.location !== -1) {
- this._renderer._enableAttrib(this, attr, size, type, normalized, stride, offset);
+ this._renderer._enableAttrib(
+ this,
+ attr,
+ size,
+ type,
+ normalized,
+ stride,
+ offset
+ );
}
}
return this;
}
-};
+}
-function shader(p5, fn){
+function shader(p5, fn) {
/**
* A class to describe a shader program.
*
@@ -1342,6 +1366,6 @@ function shader(p5, fn){
export default shader;
export { Shader };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
shader(p5, p5.prototype);
}
diff --git a/src/webgl/p5.Texture.js b/src/webgl/p5.Texture.js
index 3c12e6ed69..339787af40 100644
--- a/src/webgl/p5.Texture.js
+++ b/src/webgl/p5.Texture.js
@@ -14,7 +14,7 @@ import { Graphics } from '../core/p5.Graphics';
import { FramebufferTexture } from './p5.Framebuffer';
class Texture {
- constructor (renderer, obj, settings = {}) {
+ constructor(renderer, obj, settings = {}) {
this._renderer = renderer;
this.src = obj;
@@ -72,7 +72,8 @@ class Texture {
this.isSrcP5Image = obj instanceof Image;
this.isSrcP5Graphics = obj instanceof Graphics;
this.isSrcP5Renderer = obj instanceof Renderer;
- this.isImageData = typeof ImageData !== 'undefined' && obj instanceof ImageData;
+ this.isImageData =
+ typeof ImageData !== 'undefined' && obj instanceof ImageData;
this.isSrcMediaElement =
typeof MediaElement !== 'undefined' && obj instanceof MediaElement;
this.isSrcHTMLElement =
@@ -90,17 +91,14 @@ class Texture {
}
}
- _getTextureDataFromSource () {
+ _getTextureDataFromSource() {
let textureData;
if (this.isFramebufferTexture) {
textureData = this.src.rawTexture();
} else if (this.isSrcP5Image) {
- // param is a p5.Image
+ // param is a p5.Image
textureData = this.src.canvas;
- } else if (
- this.isSrcMediaElement ||
- this.isSrcHTMLElement
- ) {
+ } else if (this.isSrcMediaElement || this.isSrcHTMLElement) {
// createCapture elements that are flipped need
// to go through a canvas
if (this.isSrcMediaElement && this.src.flipped) {
@@ -132,10 +130,12 @@ class Texture {
format: this.format,
dataType: this.dataType,
width: textureData.width,
- height: textureData.height,
+ height: textureData.height
});
} else {
- this.textureHandle = this._renderer.createFramebufferTextureHandle(this.src);
+ this.textureHandle = this._renderer.createFramebufferTextureHandle(
+ this.src
+ );
}
this._renderer.setTextureParams(this, {
@@ -156,10 +156,7 @@ class Texture {
);
} else if (!this.isFramebufferTexture) {
// this.update()
- this._renderer.uploadTextureFromSource(
- this.textureHandle,
- textureData
- );
+ this._renderer.uploadTextureFromSource(this.textureHandle, textureData);
}
this.unbindTexture();
@@ -283,7 +280,7 @@ class Texture {
return this;
}
- unbindTexture () {
+ unbindTexture() {
this._renderer.unbindTexture();
}
@@ -343,7 +340,7 @@ class MipmapTexture extends Texture {
format: this.format,
dataType: this.dataType,
width: this.width,
- height: this.height,
+ height: this.height
});
} else {
// WebGPU path: levels is a mipmapData object with pre-built GPU texture
@@ -356,7 +353,7 @@ class MipmapTexture extends Texture {
format: levels.format,
dataType: 'uint8',
width: this.width,
- height: this.height,
+ height: this.height
});
}
@@ -371,7 +368,7 @@ class MipmapTexture extends Texture {
update() {}
}
-function texture(p5, fn){
+function texture(p5, fn) {
/**
* Texture class for WEBGL Mode
* @private
@@ -411,6 +408,6 @@ function texture(p5, fn){
export default texture;
export { Texture, MipmapTexture };
-if(typeof p5 !== 'undefined'){
+if (typeof p5 !== 'undefined') {
texture(p5, p5.prototype);
}
diff --git a/src/webgl/shaderHookUtils.js b/src/webgl/shaderHookUtils.js
index 8199746130..b274d834d7 100644
--- a/src/webgl/shaderHookUtils.js
+++ b/src/webgl/shaderHookUtils.js
@@ -17,12 +17,14 @@ export function getShaderHookTypes(shader, hookName) {
const functionName = nameParts.pop();
const returnType = nameParts.pop();
const returnQualifiers = [...nameParts];
- const parameterMatch = /\(([^\)]*)\)/.exec(body);
+ const parameterMatch = /\(([^)]*)\)/.exec(body);
if (!parameterMatch) {
throw new Error(`Couldn't find function parameters in hook body:\n${body}`);
}
const structProperties = structName => {
- const structDefMatch = new RegExp(`struct\\s+${structName}\\s*{([^}]*)}`).exec(fullSrc);
+ const structDefMatch = new RegExp(
+ `struct\\s+${structName}\\s*{([^}]*)}`
+ ).exec(fullSrc);
if (!structDefMatch) return undefined;
const properties = [];
for (const defSrc of structDefMatch[1].split(';')) {
@@ -39,7 +41,7 @@ export function getShaderHookTypes(shader, hookName) {
typeName,
qualifiers: [],
properties: typeProperties,
- dataType,
+ dataType
}
});
}
@@ -60,7 +62,7 @@ export function getShaderHookTypes(shader, hookName) {
typeName,
qualifiers,
properties,
- dataType,
+ dataType
}
};
});
@@ -71,7 +73,7 @@ export function getShaderHookTypes(shader, hookName) {
typeName: returnType,
qualifiers: returnQualifiers,
properties: structProperties(returnType),
- dataType,
+ dataType
},
parameters
};
diff --git a/src/webgl/strands_glslBackend.js b/src/webgl/strands_glslBackend.js
index 75bd44a2c1..44f9ebf7c1 100644
--- a/src/webgl/strands_glslBackend.js
+++ b/src/webgl/strands_glslBackend.js
@@ -1,8 +1,20 @@
import noiseGLSL from './shaders/functions/noise3DGLSL.glsl';
import randomGLSL from './shaders/functions/randomGLSL.glsl';
import randomVertGLSL from './shaders/functions/randomVertGLSL.glsl';
-import { NodeType, OpCodeToSymbol, BlockType, OpCode, NodeTypeToName, isStructType, BaseType, StatementType, DataType, INSTANCE_ID_VARYING_NAME, HOOK_PARAM_PREFIX } from "../strands/ir_types";
-import { getNodeDataFromID, extractNodeTypeInfo } from "../strands/ir_dag";
+import {
+ NodeType,
+ OpCodeToSymbol,
+ BlockType,
+ OpCode,
+ NodeTypeToName,
+ isStructType,
+ BaseType,
+ StatementType,
+ DataType,
+ INSTANCE_ID_VARYING_NAME,
+ HOOK_PARAM_PREFIX
+} from '../strands/ir_types';
+import { getNodeDataFromID, extractNodeTypeInfo } from '../strands/ir_dag';
import * as FES from '../strands/strands_FES';
import * as build from '../strands/ir_builders';
function shouldCreateTemp(dag, nodeID) {
@@ -13,22 +25,22 @@ function shouldCreateTemp(dag, nodeID) {
return uses.length > 1;
}
const TypeNames = {
- 'float1': 'float',
- 'float2': 'vec2',
- 'float3': 'vec3',
- 'float4': 'vec4',
- 'int1': 'int',
- 'int2': 'ivec2',
- 'int3': 'ivec3',
- 'int4': 'ivec4',
- 'bool1': 'bool',
- 'bool2': 'bvec2',
- 'bool3': 'bvec3',
- 'bool4': 'bvec4',
- 'mat2': 'mat2x2',
- 'mat3': 'mat3x3',
- 'mat4': 'mat4x4',
-}
+ float1: 'float',
+ float2: 'vec2',
+ float3: 'vec3',
+ float4: 'vec4',
+ int1: 'int',
+ int2: 'ivec2',
+ int3: 'ivec3',
+ int4: 'ivec4',
+ bool1: 'bool',
+ bool2: 'bvec2',
+ bool3: 'bvec3',
+ bool4: 'bvec4',
+ mat2: 'mat2x2',
+ mat3: 'mat3x3',
+ mat4: 'mat4x4'
+};
const cfgHandlers = {
[BlockType.DEFAULT]: (blockID, strandsContext, generationContext) => {
const { dag, cfg } = strandsContext;
@@ -36,7 +48,11 @@ const cfgHandlers = {
for (const nodeID of instructions) {
const nodeType = dag.nodeTypes[nodeID];
if (shouldCreateTemp(dag, nodeID)) {
- const declaration = glslBackend.generateDeclaration(generationContext, dag, nodeID);
+ const declaration = glslBackend.generateDeclaration(
+ generationContext,
+ dag,
+ nodeID
+ );
generationContext.write(declaration);
}
if (nodeType === NodeType.STATEMENT) {
@@ -80,7 +96,11 @@ const cfgHandlers = {
[BlockType.IF_COND](blockID, strandsContext, generationContext) {
const { dag, cfg } = strandsContext;
const conditionID = cfg.blockConditions[blockID];
- const condExpr = glslBackend.generateExpression(generationContext, dag, conditionID);
+ const condExpr = glslBackend.generateExpression(
+ generationContext,
+ dag,
+ conditionID
+ );
generationContext.write(`if (${condExpr})`);
this[BlockType.DEFAULT](blockID, strandsContext, generationContext);
},
@@ -124,7 +144,11 @@ const cfgHandlers = {
generationContext.suppressSemicolon = isLast;
if (shouldCreateTemp(dag, nodeID)) {
- const declaration = glslBackend.generateDeclaration(generationContext, dag, nodeID);
+ const declaration = glslBackend.generateDeclaration(
+ generationContext,
+ dag,
+ nodeID
+ );
generationContext.write(declaration);
}
if (node.nodeType === NodeType.STATEMENT) {
@@ -156,20 +180,26 @@ const cfgHandlers = {
const sourceNodeID = node.dependsOn[branchIndex];
const tempName = generationContext.tempNames[nodeID];
if (tempName && sourceNodeID !== null) {
- const sourceExpr = glslBackend.generateExpression(generationContext, dag, sourceNodeID);
+ const sourceExpr = glslBackend.generateExpression(
+ generationContext,
+ dag,
+ sourceNodeID
+ );
generationContext.write(`${tempName} = ${sourceExpr};`);
}
}
}
}
}
- },
-}
+ }
+};
export const glslBackend = {
hookEntry(hookType) {
- const firstLine = `(${hookType.parameters.flatMap((param) => {
- return `${param.qualifiers?.length ? param.qualifiers.join(' ') : ''}${param.type.typeName} ${HOOK_PARAM_PREFIX}${param.name}`;
- }).join(', ')}) {`;
+ const firstLine = `(${hookType.parameters
+ .flatMap(param => {
+ return `${param.qualifiers?.length ? param.qualifiers.join(' ') : ''}${param.type.typeName} ${HOOK_PARAM_PREFIX}${param.name}`;
+ })
+ .join(', ')}) {`;
return firstLine;
},
getNoiseShaderSnippet() {
@@ -182,7 +212,7 @@ export const glslBackend = {
return randomVertGLSL;
},
getTypeName(baseType, dimension) {
- const primitiveTypeName = TypeNames[baseType + dimension]
+ const primitiveTypeName = TypeNames[baseType + dimension];
if (!primitiveTypeName) {
return baseType;
}
@@ -225,8 +255,16 @@ export const glslBackend = {
const sourceNodeID = node.dependsOn[1];
// Generate the target expression (could be variable or swizzle)
- const targetExpr = this.generateExpression(generationContext, dag, targetNodeID);
- const sourceExpr = this.generateExpression(generationContext, dag, sourceNodeID);
+ const targetExpr = this.generateExpression(
+ generationContext,
+ dag,
+ targetNodeID
+ );
+ const sourceExpr = this.generateExpression(
+ generationContext,
+ dag,
+ sourceNodeID
+ );
const semicolon = generationContext.suppressSemicolon ? '' : ';';
// Generate assignment if we have both target and source
@@ -242,7 +280,12 @@ export const glslBackend = {
const typeName = this.getTypeName(T.baseType, T.dimension);
return `${typeName} ${tmp} = ${expr};`;
},
- generateReturnStatement(strandsContext, generationContext, rootNodeID, returnType) {
+ generateReturnStatement(
+ strandsContext,
+ generationContext,
+ rootNodeID,
+ returnType
+ ) {
if (!returnType) {
generationContext.write('return;');
return;
@@ -253,15 +296,21 @@ export const glslBackend = {
const structTypeInfo = returnType;
for (let i = 0; i < structTypeInfo.properties.length; i++) {
const prop = structTypeInfo.properties[i];
- const val = this.generateExpression(generationContext, dag, rootNode.dependsOn[i]);
+ const val = this.generateExpression(
+ generationContext,
+ dag,
+ rootNode.dependsOn[i]
+ );
if (prop.name !== val) {
generationContext.write(
`${rootNode.identifier}.${prop.name} = ${val};`
- )
+ );
}
}
}
- generationContext.write(`return ${this.generateExpression(generationContext, dag, rootNodeID)};`);
+ generationContext.write(
+ `return ${this.generateExpression(generationContext, dag, rootNodeID)};`
+ );
},
generateExpression(generationContext, dag, nodeID) {
const node = getNodeDataFromID(dag, nodeID);
@@ -270,136 +319,184 @@ export const glslBackend = {
}
switch (node.nodeType) {
case NodeType.LITERAL:
- if (node.baseType === BaseType.FLOAT) {
- return node.value.toFixed(4);
- }
- else {
- return node.value;
- }
+ if (node.baseType === BaseType.FLOAT) {
+ return node.value.toFixed(4);
+ } else {
+ return node.value;
+ }
case NodeType.VARIABLE:
- // Track shared variable usage context
- if (generationContext.shaderContext && generationContext.strandsContext?.sharedVariables?.has(node.identifier)) {
- const sharedVar = generationContext.strandsContext.sharedVariables.get(node.identifier);
- if (generationContext.shaderContext === 'vertex') {
- sharedVar.usedInVertex = true;
- } else if (generationContext.shaderContext === 'fragment') {
- sharedVar.usedInFragment = true;
+ // Track shared variable usage context
+ if (
+ generationContext.shaderContext &&
+ generationContext.strandsContext?.sharedVariables?.has(
+ node.identifier
+ )
+ ) {
+ const sharedVar =
+ generationContext.strandsContext.sharedVariables.get(
+ node.identifier
+ );
+ if (generationContext.shaderContext === 'vertex') {
+ sharedVar.usedInVertex = true;
+ } else if (generationContext.shaderContext === 'fragment') {
+ sharedVar.usedInFragment = true;
+ }
}
- }
- // Detect instanceID usage in fragment context and rewrite to varying name
- if (node.identifier === this.instanceIdReference() && generationContext.shaderContext === 'fragment') {
- generationContext.strandsContext._instanceIDUsedInFragment = true;
- return INSTANCE_ID_VARYING_NAME;
- }
+ // Detect instanceID usage in fragment context and rewrite to varying name
+ if (
+ node.identifier === this.instanceIdReference() &&
+ generationContext.shaderContext === 'fragment'
+ ) {
+ generationContext.strandsContext._instanceIDUsedInFragment = true;
+ return INSTANCE_ID_VARYING_NAME;
+ }
- return node.identifier;
+ return node.identifier;
case NodeType.OPERATION:
- const useParantheses = node.usedBy.length > 0;
- if (node.opCode === OpCode.Nary.CONSTRUCTOR) {
- // TODO: differentiate casts and constructors for more efficient codegen.
- // if (node.dependsOn.length === 1 && node.dimension === 1) {
- // return this.generateExpression(generationContext, dag, node.dependsOn[0]);
- // }
- if (node.baseType === BaseType.SAMPLER2D) {
- return this.generateExpression(generationContext, dag, node.dependsOn[0]);
+ const useParantheses = node.usedBy.length > 0;
+ if (node.opCode === OpCode.Nary.CONSTRUCTOR) {
+ // TODO: differentiate casts and constructors for more efficient codegen.
+ // if (node.dependsOn.length === 1 && node.dimension === 1) {
+ // return this.generateExpression(generationContext, dag, node.dependsOn[0]);
+ // }
+ if (node.baseType === BaseType.SAMPLER2D) {
+ return this.generateExpression(
+ generationContext,
+ dag,
+ node.dependsOn[0]
+ );
+ }
+ const T = this.getTypeName(node.baseType, node.dimension);
+ const deps = node.dependsOn.map(dep =>
+ this.generateExpression(generationContext, dag, dep)
+ );
+ return `${T}(${deps.join(', ')})`;
}
- const T = this.getTypeName(node.baseType, node.dimension);
- const deps = node.dependsOn.map((dep) => this.generateExpression(generationContext, dag, dep));
- return `${T}(${deps.join(', ')})`;
- }
- if (node.opCode === OpCode.Nary.FUNCTION_CALL) {
- const functionArgs = node.dependsOn.map(arg =>this.generateExpression(generationContext, dag, arg));
- return `${node.identifier}(${functionArgs.join(', ')})`;
- }
- if (node.opCode === OpCode.Nary.TERNARY) {
- const [condID, trueID, falseID] = node.dependsOn;
- const cond = this.generateExpression(generationContext, dag, condID);
- const trueExpr = this.generateExpression(generationContext, dag, trueID);
- const falseExpr = this.generateExpression(generationContext, dag, falseID);
- return `(${cond} ? ${trueExpr} : ${falseExpr})`;
- }
- if (node.opCode === OpCode.Binary.MEMBER_ACCESS) {
- const [lID, rID] = node.dependsOn;
- const lName = this.generateExpression(generationContext, dag, lID);
- const rName = this.generateExpression(generationContext, dag, rID);
- return `${lName}.${rName}`;
- }
- if (node.opCode === OpCode.Unary.SWIZZLE) {
- const parentID = node.dependsOn[0];
- const parentExpr = this.generateExpression(generationContext, dag, parentID);
- return `${parentExpr}.${node.swizzle}`;
- }
- if (node.opCode === OpCode.Binary.ARRAY_ACCESS) {
- const [bufferID, indexID] = node.dependsOn;
- const bufferExpr = this.generateExpression(generationContext, dag, bufferID);
- const indexExpr = this.generateExpression(generationContext, dag, indexID);
- return `${bufferExpr}[${indexExpr}]`;
- }
- if (node.dependsOn.length === 2) {
- const [lID, rID] = node.dependsOn;
- const left = this.generateExpression(generationContext, dag, lID);
- const right = this.generateExpression(generationContext, dag, rID);
+ if (node.opCode === OpCode.Nary.FUNCTION_CALL) {
+ const functionArgs = node.dependsOn.map(arg =>
+ this.generateExpression(generationContext, dag, arg)
+ );
+ return `${node.identifier}(${functionArgs.join(', ')})`;
+ }
+ if (node.opCode === OpCode.Nary.TERNARY) {
+ const [condID, trueID, falseID] = node.dependsOn;
+ const cond = this.generateExpression(generationContext, dag, condID);
+ const trueExpr = this.generateExpression(
+ generationContext,
+ dag,
+ trueID
+ );
+ const falseExpr = this.generateExpression(
+ generationContext,
+ dag,
+ falseID
+ );
+ return `(${cond} ? ${trueExpr} : ${falseExpr})`;
+ }
+ if (node.opCode === OpCode.Binary.MEMBER_ACCESS) {
+ const [lID, rID] = node.dependsOn;
+ const lName = this.generateExpression(generationContext, dag, lID);
+ const rName = this.generateExpression(generationContext, dag, rID);
+ return `${lName}.${rName}`;
+ }
+ if (node.opCode === OpCode.Unary.SWIZZLE) {
+ const parentID = node.dependsOn[0];
+ const parentExpr = this.generateExpression(
+ generationContext,
+ dag,
+ parentID
+ );
+ return `${parentExpr}.${node.swizzle}`;
+ }
+ if (node.opCode === OpCode.Binary.ARRAY_ACCESS) {
+ const [bufferID, indexID] = node.dependsOn;
+ const bufferExpr = this.generateExpression(
+ generationContext,
+ dag,
+ bufferID
+ );
+ const indexExpr = this.generateExpression(
+ generationContext,
+ dag,
+ indexID
+ );
+ return `${bufferExpr}[${indexExpr}]`;
+ }
+ if (node.dependsOn.length === 2) {
+ const [lID, rID] = node.dependsOn;
+ const left = this.generateExpression(generationContext, dag, lID);
+ const right = this.generateExpression(generationContext, dag, rID);
- // Special case for modulo: use mod() function for floats in GLSL
- if (node.opCode === OpCode.Binary.MODULO) {
- const leftNode = getNodeDataFromID(dag, lID);
- const rightNode = getNodeDataFromID(dag, rID);
- // If either operand is float, use mod() function
- if (leftNode.baseType === BaseType.FLOAT || rightNode.baseType === BaseType.FLOAT) {
- return `mod(${left}, ${right})`;
+ // Special case for modulo: use mod() function for floats in GLSL
+ if (node.opCode === OpCode.Binary.MODULO) {
+ const leftNode = getNodeDataFromID(dag, lID);
+ const rightNode = getNodeDataFromID(dag, rID);
+ // If either operand is float, use mod() function
+ if (
+ leftNode.baseType === BaseType.FLOAT ||
+ rightNode.baseType === BaseType.FLOAT
+ ) {
+ return `mod(${left}, ${right})`;
+ }
+ // For integers, use % operator
+ return `(${left} % ${right})`;
}
- // For integers, use % operator
- return `(${left} % ${right})`;
- }
- const opSym = OpCodeToSymbol[node.opCode];
- if (useParantheses) {
- return `(${left} ${opSym} ${right})`;
- } else {
- return `${left} ${opSym} ${right}`;
+ const opSym = OpCodeToSymbol[node.opCode];
+ if (useParantheses) {
+ return `(${left} ${opSym} ${right})`;
+ } else {
+ return `${left} ${opSym} ${right}`;
+ }
}
- }
- if (node.opCode === OpCode.Unary.LOGICAL_NOT
- || node.opCode === OpCode.Unary.NEGATE
- || node.opCode === OpCode.Unary.PLUS
+ if (
+ node.opCode === OpCode.Unary.LOGICAL_NOT ||
+ node.opCode === OpCode.Unary.NEGATE ||
+ node.opCode === OpCode.Unary.PLUS
) {
- const [i] = node.dependsOn;
- const val = this.generateExpression(generationContext, dag, i);
- const sym = OpCodeToSymbol[node.opCode];
- return `${sym}${val}`;
- }
+ const [i] = node.dependsOn;
+ const val = this.generateExpression(generationContext, dag, i);
+ const sym = OpCodeToSymbol[node.opCode];
+ return `${sym}${val}`;
+ }
case NodeType.PHI:
- // Phi nodes represent conditional merging of values
- // If this phi node has an identifier (like varying variables), use that
- if (node.identifier) {
- return node.identifier;
- }
- // Otherwise, they should have been declared as temporary variables
- // and assigned in the appropriate branches
- if (generationContext.tempNames?.[nodeID]) {
- return generationContext.tempNames[nodeID];
- } else {
- // If no temp was created, this phi node only has one input
- // so we can just use that directly
- const validInputs = node.dependsOn.filter(id => id !== null);
- if (validInputs.length > 0) {
- return this.generateExpression(generationContext, dag, validInputs[0]);
+ // Phi nodes represent conditional merging of values
+ // If this phi node has an identifier (like varying variables), use that
+ if (node.identifier) {
+ return node.identifier;
+ }
+ // Otherwise, they should have been declared as temporary variables
+ // and assigned in the appropriate branches
+ if (generationContext.tempNames?.[nodeID]) {
+ return generationContext.tempNames[nodeID];
} else {
- throw new Error(`No valid inputs for node`)
- // Fallback: create a default value
- const typeName = this.getTypeName(node.baseType, node.dimension);
- if (node.dimension === 1) {
- return node.baseType === BaseType.FLOAT ? '0.0' : '0';
+ // If no temp was created, this phi node only has one input
+ // so we can just use that directly
+ const validInputs = node.dependsOn.filter(id => id !== null);
+ if (validInputs.length > 0) {
+ return this.generateExpression(
+ generationContext,
+ dag,
+ validInputs[0]
+ );
} else {
- return `${typeName}(0.0)`;
+ throw new Error(`No valid inputs for node`);
+ // Fallback: create a default value
+ const typeName = this.getTypeName(node.baseType, node.dimension);
+ if (node.dimension === 1) {
+ return node.baseType === BaseType.FLOAT ? '0.0' : '0';
+ } else {
+ return `${typeName}(0.0)`;
+ }
}
}
- }
case NodeType.ASSIGNMENT:
- FES.internalError(`ASSIGNMENT nodes should not be used as expressions`)
+ FES.internalError(`ASSIGNMENT nodes should not be used as expressions`);
default:
- FES.internalError(`${NodeTypeToName[node.nodeType]} code generation not implemented yet`)
+ FES.internalError(
+ `${NodeTypeToName[node.nodeType]} code generation not implemented yet`
+ );
}
},
generateBlock(blockID, strandsContext, generationContext) {
@@ -411,12 +508,19 @@ export const glslBackend = {
createGetTextureCall(strandsContext, args) {
// In GLSL, getTexture is straightforward - just pass through the args
// First argument should be a texture (sampler2D), second should be coordinates
- const { id, dimension } = build.functionCallNode(strandsContext, 'getTexture', args, {
- overloads: [{
- params: [DataType.sampler2D, DataType.float2],
- returnType: DataType.float4
- }]
- });
+ const { id, dimension } = build.functionCallNode(
+ strandsContext,
+ 'getTexture',
+ args,
+ {
+ overloads: [
+ {
+ params: [DataType.sampler2D, DataType.float2],
+ returnType: DataType.float4
+ }
+ ]
+ }
+ );
return { id, dimension };
},
@@ -425,6 +529,11 @@ export const glslBackend = {
},
generateInstanceIDVarying() {
- return { name: INSTANCE_ID_VARYING_NAME, declaration: `int ${INSTANCE_ID_VARYING_NAME}`, source: 'gl_InstanceID', interpolation: 'flat' };
- },
-}
+ return {
+ name: INSTANCE_ID_VARYING_NAME,
+ declaration: `int ${INSTANCE_ID_VARYING_NAME}`,
+ source: 'gl_InstanceID',
+ interpolation: 'flat'
+ };
+ }
+};
diff --git a/src/webgl/text.js b/src/webgl/text.js
index 2a2f5d66d3..f10c8e8ea1 100644
--- a/src/webgl/text.js
+++ b/src/webgl/text.js
@@ -5,12 +5,12 @@ import { Geometry } from './p5.Geometry';
import { Font, arrayCommandsToObjects } from '../type/p5.Font';
function text(p5, fn) {
- Renderer3D.prototype.maxCachedGlyphs = function() {
+ Renderer3D.prototype.maxCachedGlyphs = function () {
// TODO: use more than vibes to find a good value for this
return 200;
};
- Font.prototype._getFontInfo = function(axs) {
+ Font.prototype._getFontInfo = function (axs) {
// For WebGL, a cache of font data to use on the GPU.
this._fontInfos = this._fontInfos || {};
@@ -241,7 +241,7 @@ function text(p5, fn) {
* find the minimum & maximum value in a list of values
*/
function minMax(rg, min, max) {
- for (let i = rg.length; i-- > 0; ) {
+ for (let i = rg.length; i-- > 0;) {
const v = rg[i];
if (min > v) min = v;
if (max < v) max = v;
diff --git a/src/webgl/utils.js b/src/webgl/utils.js
index 2d79b14578..0d29e143e4 100644
--- a/src/webgl/utils.js
+++ b/src/webgl/utils.js
@@ -1,6 +1,6 @@
-import * as constants from "../core/constants";
-import { INSTANCE_ID_VARYING_NAME } from "../strands/ir_types";
-import { Texture } from "./p5.Texture";
+import * as constants from '../core/constants';
+import { INSTANCE_ID_VARYING_NAME } from '../strands/ir_types';
+import { Texture } from './p5.Texture';
/**
* @private
@@ -27,7 +27,7 @@ export function readPixelsWebGL(
height,
format,
type,
- flipY,
+ flipY
) {
// Record the currently bound framebuffer so we can go back to it after, and
// bind the framebuffer we want to read from
@@ -50,7 +50,7 @@ export function readPixelsWebGL(
height,
format,
type,
- pixels,
+ pixels
);
// Re-bind whatever was previously bound
@@ -105,15 +105,18 @@ export function readPixelWebGL(gl, framebuffer, x, y, format, type, flipY) {
export function setWebGLTextureParams(texture, gl, webglVersion) {
texture.bindTexture();
const glMinFilter =
- texture.minFilter === constants.NEAREST ? gl.NEAREST :
- texture.minFilter === constants.LINEAR_MIPMAP ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR;
+ texture.minFilter === constants.NEAREST
+ ? gl.NEAREST
+ : texture.minFilter === constants.LINEAR_MIPMAP
+ ? gl.LINEAR_MIPMAP_LINEAR
+ : gl.LINEAR;
const glMagFilter =
texture.magFilter === constants.NEAREST ? gl.NEAREST : gl.LINEAR;
// for webgl 1 we need to check if the texture is power of two
// if it isn't we will set the wrap mode to CLAMP
// webgl2 will support npot REPEAT and MIRROR but we don't check for it yet
- const isPowerOfTwo = (x) => (x & (x - 1)) === 0;
+ const isPowerOfTwo = x => (x & (x - 1)) === 0;
const textureData = texture._getTextureDataFromSource();
let wrapWidth;
@@ -139,7 +142,7 @@ export function setWebGLTextureParams(texture, gl, webglVersion) {
glWrapS = gl.REPEAT;
} else {
console.warn(
- "You tried to set the wrap mode to REPEAT but the texture size is not a power of two. Setting to CLAMP instead",
+ 'You tried to set the wrap mode to REPEAT but the texture size is not a power of two. Setting to CLAMP instead'
);
glWrapS = gl.CLAMP_TO_EDGE;
}
@@ -151,7 +154,7 @@ export function setWebGLTextureParams(texture, gl, webglVersion) {
glWrapS = gl.MIRRORED_REPEAT;
} else {
console.warn(
- "You tried to set the wrap mode to MIRROR but the texture size is not a power of two. Setting to CLAMP instead",
+ 'You tried to set the wrap mode to MIRROR but the texture size is not a power of two. Setting to CLAMP instead'
);
glWrapS = gl.CLAMP_TO_EDGE;
}
@@ -168,7 +171,7 @@ export function setWebGLTextureParams(texture, gl, webglVersion) {
glWrapT = gl.REPEAT;
} else {
console.warn(
- "You tried to set the wrap mode to REPEAT but the texture size is not a power of two. Setting to CLAMP instead",
+ 'You tried to set the wrap mode to REPEAT but the texture size is not a power of two. Setting to CLAMP instead'
);
glWrapT = gl.CLAMP_TO_EDGE;
}
@@ -180,7 +183,7 @@ export function setWebGLTextureParams(texture, gl, webglVersion) {
glWrapT = gl.MIRRORED_REPEAT;
} else {
console.warn(
- "You tried to set the wrap mode to MIRROR but the texture size is not a power of two. Setting to CLAMP instead",
+ 'You tried to set the wrap mode to MIRROR but the texture size is not a power of two. Setting to CLAMP instead'
);
glWrapT = gl.CLAMP_TO_EDGE;
}
@@ -274,16 +277,16 @@ export function setWebGLUniformValue(shader, uniform, data, getTexture, gl) {
}
break;
case gl.SAMPLER_2D:
- if (typeof data == "number") {
+ if (typeof data == 'number') {
if (
data < gl.TEXTURE0 ||
data > gl.TEXTURE31 ||
data !== Math.ceil(data)
) {
console.log(
- "🌸 p5.js says: " +
+ '🌸 p5.js says: ' +
"You're trying to use a number as the data for a texture." +
- "Please use a texture.",
+ 'Please use a texture.'
);
return;
}
@@ -309,7 +312,7 @@ export function setWebGLUniformValue(shader, uniform, data, getTexture, gl) {
case gl.UNSIGNED_INT_SAMPLER_3D:
case gl.UNSIGNED_INT_SAMPLER_CUBE:
case gl.UNSIGNED_INT_SAMPLER_2D_ARRAY:
- if (typeof data !== "number") {
+ if (typeof data !== 'number') {
break;
}
if (
@@ -318,9 +321,9 @@ export function setWebGLUniformValue(shader, uniform, data, getTexture, gl) {
data !== Math.ceil(data)
) {
console.log(
- "🌸 p5.js says: " +
+ '🌸 p5.js says: ' +
"You're trying to use a number as the data for a texture." +
- "Please use a texture.",
+ 'Please use a texture.'
);
break;
}
@@ -350,7 +353,7 @@ export function getWebGLUniformMetadata(shader, gl) {
//off here. The size property tells us that its an array
//so we dont lose any information by doing this
if (uniformInfo.size > 1) {
- uniformName = uniformName.substring(0, uniformName.indexOf("[0]"));
+ uniformName = uniformName.substring(0, uniformName.indexOf('[0]'));
}
uniform.name = uniformName;
uniform.type = uniformInfo.type;
@@ -384,7 +387,7 @@ export function getWebGLShaderAttributes(shader, gl) {
const numAttributes = gl.getProgramParameter(
shader._glProgram,
- gl.ACTIVE_ATTRIBUTES,
+ gl.ACTIVE_ATTRIBUTES
);
for (let i = 0; i < numAttributes; ++i) {
const attributeInfo = gl.getActiveAttrib(shader._glProgram, i);
@@ -403,21 +406,21 @@ export function getWebGLShaderAttributes(shader, gl) {
}
export function populateGLSLHooks(shader, src, shaderType) {
- const main = "void main";
+ const main = 'void main';
if (!src.includes(main)) return src;
let [preMain, postMain] = src.split(main);
- let hooks = "";
- let defines = "";
+ let hooks = '';
+ let defines = '';
for (const key in shader.hooks.uniforms) {
hooks += `uniform ${key};\n`;
}
if (shader.hooks.declarations) {
- hooks += shader.hooks.declarations + "\n";
+ hooks += shader.hooks.declarations + '\n';
}
if (shader.hooks[shaderType].declarations) {
- hooks += shader.hooks[shaderType].declarations + "\n";
+ hooks += shader.hooks[shaderType].declarations + '\n';
}
// Handle varying variables from p5.strands
@@ -427,9 +430,9 @@ export function populateGLSLHooks(shader, src, shaderType) {
) {
for (const varyingVar of shader.hooks.varyingVariables) {
// Generate OUT declaration for vertex shader, IN declaration for fragment shader
- if (shaderType === "vertex") {
+ if (shaderType === 'vertex') {
hooks += `OUT ${varyingVar};\n`;
- } else if (shaderType === "fragment") {
+ } else if (shaderType === 'fragment') {
hooks += `IN ${varyingVar};\n`;
}
}
@@ -437,13 +440,17 @@ export function populateGLSLHooks(shader, src, shaderType) {
// Handle instanceID varying for fragment access
if (shader.hooks.instanceIDVarying) {
- const { declaration, source, interpolation } = shader.hooks.instanceIDVarying;
+ const { declaration, source, interpolation } =
+ shader.hooks.instanceIDVarying;
const qualifier = interpolation ? `${interpolation} ` : '';
- if (shaderType === "vertex") {
+ if (shaderType === 'vertex') {
// Emit flat out declaration and inject assignment into main() body
hooks += `${qualifier}OUT ${declaration};\n`;
- postMain = postMain.replace(/\{/, `{\n ${declaration.split(' ').pop()} = ${source};`);
- } else if (shaderType === "fragment") {
+ postMain = postMain.replace(
+ /\{/,
+ `{\n ${declaration.split(' ').pop()} = ${source};`
+ );
+ } else if (shaderType === 'fragment') {
hooks += `${qualifier}IN ${declaration};\n`;
}
}
@@ -452,8 +459,8 @@ export function populateGLSLHooks(shader, src, shaderType) {
hooks += `${hookDef}${shader.hooks.helpers[hookDef]}\n`;
}
for (const hookDef in shader.hooks[shaderType]) {
- if (hookDef === "declarations") continue;
- const [hookType, hookName] = hookDef.split(" ");
+ if (hookDef === 'declarations') continue;
+ const [hookType, hookName] = hookDef.split(' ');
// Add a #define so that if the shader wants to use preprocessor directives to
// optimize away the extra function calls in main, it can do so
@@ -461,43 +468,43 @@ export function populateGLSLHooks(shader, src, shaderType) {
shader.hooks.modified.vertex[hookDef] ||
shader.hooks.modified.fragment[hookDef]
) {
- defines += "#define AUGMENTED_HOOK_" + hookName + "\n";
+ defines += '#define AUGMENTED_HOOK_' + hookName + '\n';
}
hooks +=
- hookType + " HOOK_" + hookName + shader.hooks[shaderType][hookDef] + "\n";
+ hookType + ' HOOK_' + hookName + shader.hooks[shaderType][hookDef] + '\n';
}
// Allow shaders to specify the location of hook #define statements. Normally these
// go after function definitions, but one might want to have them defined earlier
// in order to only conditionally make uniforms.
- if (preMain.indexOf("#define HOOK_DEFINES") !== -1) {
- preMain = preMain.replace("#define HOOK_DEFINES", "\n" + defines + "\n");
- defines = "";
+ if (preMain.indexOf('#define HOOK_DEFINES') !== -1) {
+ preMain = preMain.replace('#define HOOK_DEFINES', '\n' + defines + '\n');
+ defines = '';
}
- return preMain + "\n" + defines + hooks + main + postMain;
+ return preMain + '\n' + defines + hooks + main + postMain;
}
export function checkWebGLCapabilities({ GL, webglVersion }) {
const gl = GL;
const supportsFloat =
webglVersion === constants.WEBGL2
- ? gl.getExtension("EXT_color_buffer_float") &&
- gl.getExtension("EXT_float_blend")
- : gl.getExtension("OES_texture_float");
+ ? gl.getExtension('EXT_color_buffer_float') &&
+ gl.getExtension('EXT_float_blend')
+ : gl.getExtension('OES_texture_float');
const supportsFloatLinear =
- supportsFloat && gl.getExtension("OES_texture_float_linear");
+ supportsFloat && gl.getExtension('OES_texture_float_linear');
const supportsHalfFloat =
webglVersion === constants.WEBGL2
- ? gl.getExtension("EXT_color_buffer_float")
- : gl.getExtension("OES_texture_half_float");
+ ? gl.getExtension('EXT_color_buffer_float')
+ : gl.getExtension('OES_texture_half_float');
const supportsHalfFloatLinear =
- supportsHalfFloat && gl.getExtension("OES_texture_half_float_linear");
+ supportsHalfFloat && gl.getExtension('OES_texture_half_float_linear');
return {
float: supportsFloat,
floatLinear: supportsFloatLinear,
halfFloat: supportsHalfFloat,
- halfFloatLinear: supportsHalfFloatLinear,
+ halfFloatLinear: supportsHalfFloatLinear
};
}
diff --git a/src/webgpu/index.js b/src/webgpu/index.js
index fb7670482e..1865aac965 100644
--- a/src/webgpu/index.js
+++ b/src/webgpu/index.js
@@ -1,5 +1,5 @@
import rendererWebGPU from './p5.RendererWebGPU';
-export default function(p5){
+export default function (p5) {
p5.registerAddon(rendererWebGPU);
}
diff --git a/src/webgpu/p5.RendererWebGPU.js b/src/webgpu/p5.RendererWebGPU.js
index 565241fda6..acf096bd96 100644
--- a/src/webgpu/p5.RendererWebGPU.js
+++ b/src/webgpu/p5.RendererWebGPU.js
@@ -9,14 +9,24 @@ import { getStrokeDefs } from '../webgl/enums';
import { DataType, INSTANCE_ID_VARYING_NAME } from '../strands/ir_types.js';
import { colorVertexShader, colorFragmentShader } from './shaders/color';
-import { lineVertexShader, lineFragmentShader} from './shaders/line';
-import { materialVertexShader, materialFragmentShader } from './shaders/material';
+import { lineVertexShader, lineFragmentShader } from './shaders/line';
+import {
+ materialVertexShader,
+ materialFragmentShader
+} from './shaders/material';
import { fontVertexShader, fontFragmentShader } from './shaders/font';
import { blitVertexShader, blitFragmentShader } from './shaders/blit';
import { wgslBackend } from './strands_wgslBackend';
-import { baseFilterVertexShader, baseFilterFragmentShader } from './shaders/filters/base';
-import { imageLightVertexShader, imageLightDiffusedFragmentShader, imageLightSpecularFragmentShader } from './shaders/imageLight';
+import {
+ baseFilterVertexShader,
+ baseFilterFragmentShader
+} from './shaders/filters/base';
+import {
+ imageLightVertexShader,
+ imageLightDiffusedFragmentShader,
+ imageLightSpecularFragmentShader
+} from './shaders/imageLight';
import { baseComputeShader } from './shaders/compute';
const FRAME_STATE = {
@@ -30,15 +40,8 @@ function rendererWebGPU(p5, fn) {
// RendererWebGPU depends on these other classes being set up prior,
// as it is optimized for being in a standalone build, not core
- const {
- Renderer3D,
- Shader,
- Texture,
- MipmapTexture,
- Image,
- Camera,
- RGBA,
- } = p5;
+ const { Renderer3D, Shader, Texture, MipmapTexture, Image, Camera, RGBA } =
+ p5;
class StorageBuffer {
constructor(buffer, size, renderer, schema = null) {
@@ -135,8 +138,8 @@ function rendererWebGPU(p5, fn) {
if (newSchema.structBody !== this._schema.structBody) {
throw new Error(
`update() data structure doesn't match the original.\n` +
- ` Expected: ${this._schema.structBody}\n` +
- ` Got: ${newSchema.structBody}`
+ ` Expected: ${this._schema.structBody}\n` +
+ ` Got: ${newSchema.structBody}`
);
}
@@ -218,11 +221,17 @@ function rendererWebGPU(p5, fn) {
const stagingBuffer = device.createBuffer({
size: this.size,
- usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
});
const commandEncoder = device.createCommandEncoder();
- commandEncoder.copyBufferToBuffer(this.buffer, 0, stagingBuffer, 0, this.size);
+ commandEncoder.copyBufferToBuffer(
+ this.buffer,
+ 0,
+ stagingBuffer,
+ 0,
+ this.size
+ );
device.queue.submit([commandEncoder.finish()]);
await stagingBuffer.mapAsync(GPUMapMode.READ, 0, this.size);
@@ -310,7 +319,11 @@ function rendererWebGPU(p5, fn) {
if (this._schema !== null) {
// buffer was created with an array of structs
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
+ if (
+ typeof value !== 'object' ||
+ value === null ||
+ Array.isArray(value)
+ ) {
throw new Error(
'set() expects a plain object matching the original struct format for this buffer'
);
@@ -322,20 +335,24 @@ function rendererWebGPU(p5, fn) {
if (byteOffset + stride > this.size) {
throw new Error(
`set() index ${index} is out of bounds for this buffer ` +
- `(buffer holds ${Math.floor(this.size / stride)} elements)`
+ `(buffer holds ${Math.floor(this.size / stride)} elements)`
);
}
// pack just this one element using the same logic as update()
const packed = this._renderer._packStructArray([value], this._schema);
// use packed.buffer (ArrayBuffer) so the size arg is always in bytes
- device.queue.writeBuffer(this.buffer, byteOffset, packed.buffer, 0, stride);
+ device.queue.writeBuffer(
+ this.buffer,
+ byteOffset,
+ packed.buffer,
+ 0,
+ stride
+ );
} else {
// buffer was created with a float array
if (typeof value !== 'number') {
- throw new Error(
- 'set() expects a number for this float buffer'
- );
+ throw new Error('set() expects a number for this float buffer');
}
const byteOffset = index * 4;
@@ -343,11 +360,15 @@ function rendererWebGPU(p5, fn) {
if (byteOffset + 4 > this.size) {
throw new Error(
`set() index ${index} is out of bounds for this buffer ` +
- `(buffer holds ${Math.floor(this.size / 4)} floats)`
+ `(buffer holds ${Math.floor(this.size / 4)} floats)`
);
}
- device.queue.writeBuffer(this.buffer, byteOffset, new Float32Array([value]));
+ device.queue.writeBuffer(
+ this.buffer,
+ byteOffset,
+ new Float32Array([value])
+ );
}
}
}
@@ -368,7 +389,7 @@ function rendererWebGPU(p5, fn) {
class RendererWebGPU extends Renderer3D {
constructor(pInst, w, h, isMainCanvas, elt) {
- super(pInst, w, h, isMainCanvas, elt)
+ super(pInst, w, h, isMainCanvas, elt);
// Used to group draws into one big render pass
this.activeRenderPass = null;
@@ -443,18 +464,23 @@ function rendererWebGPU(p5, fn) {
_setAttributeDefaults(pInst) {
const defaults = {
forceFallbackAdapter: false,
- powerPreference: 'high-performance',
+ powerPreference: 'high-performance'
};
if (pInst._webgpuAttributes === null) {
pInst._webgpuAttributes = defaults;
} else {
- pInst._webgpuAttributes = Object.assign(defaults, pInst._webgpuAttributes);
+ pInst._webgpuAttributes = Object.assign(
+ defaults,
+ pInst._webgpuAttributes
+ );
}
return;
}
async _initContext() {
- this.adapter = await navigator.gpu?.requestAdapter(this._webgpuAttributes);
+ this.adapter = await navigator.gpu?.requestAdapter(
+ this._webgpuAttributes
+ );
this.device = await this.adapter?.requestDevice({
// Todo: check support
requiredFeatures: ['depth32float-stencil8']
@@ -469,7 +495,7 @@ function rendererWebGPU(p5, fn) {
device: this.device,
format: this.presentationFormat,
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
- alphaMode: 'premultiplied',
+ alphaMode: 'premultiplied'
});
// TODO disablable stencil
@@ -480,16 +506,16 @@ function rendererWebGPU(p5, fn) {
}
async _setAttributes(key, value) {
- if (typeof this._pInst._webgpuAttributes === "undefined") {
+ if (typeof this._pInst._webgpuAttributes === 'undefined') {
console.log(
- "You are trying to use setAttributes on a p5.Graphics object " +
- "that does not use a WebGPU renderer."
+ 'You are trying to use setAttributes on a p5.Graphics object ' +
+ 'that does not use a WebGPU renderer.'
);
return;
}
let unchanged = true;
- if (typeof value !== "undefined") {
+ if (typeof value !== 'undefined') {
//first time modifying the attributes
if (this._pInst._webgpuAttributes === null) {
this._pInst._webgpuAttributes = {};
@@ -513,7 +539,8 @@ function rendererWebGPU(p5, fn) {
if (!this._pInst._setupDone) {
if (this.geometryBufferCache.numCached() > 0) {
- p5.FES.log`Sorry, Could not set the attributes, you need to call setAttributes() before calling the other drawing methods in setup()`();
+ p5.FES
+ .log`Sorry, Could not set the attributes, you need to call setAttributes() before calling the other drawing methods in setup()`();
return;
}
}
@@ -537,10 +564,10 @@ function rendererWebGPU(p5, fn) {
size: {
width: Math.ceil(this.width * this._pixelDensity),
height: Math.ceil(this.height * this._pixelDensity),
- depthOrArrayLayers: 1,
+ depthOrArrayLayers: 1
},
format: this.depthFormat,
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
});
this.depthTextureView = this.depthTexture.createView();
@@ -566,38 +593,39 @@ function rendererWebGPU(p5, fn) {
const colorAttachment = {
view: activeFramebuffer
- ? (activeFramebuffer.aaColorTexture
- ? activeFramebuffer.aaColorTextureView
- : activeFramebuffer.colorTextureView)
+ ? activeFramebuffer.aaColorTexture
+ ? activeFramebuffer.aaColorTextureView
+ : activeFramebuffer.colorTextureView
: this._getCanvasColorTextureView(),
- loadOp: "load",
- storeOp: "store",
+ loadOp: 'load',
+ storeOp: 'store',
// If using multisampled texture, resolve to non-multisampled texture
- resolveTarget: activeFramebuffer && activeFramebuffer.aaColorTexture
- ? activeFramebuffer.colorTextureView
- : undefined,
+ resolveTarget:
+ activeFramebuffer && activeFramebuffer.aaColorTexture
+ ? activeFramebuffer.colorTextureView
+ : undefined
};
// Use framebuffer depth texture if active, otherwise use canvas depth texture
const depthTextureView = activeFramebuffer
- ? (activeFramebuffer.aaDepthTexture
- ? activeFramebuffer.aaDepthTextureView
- : activeFramebuffer.depthTextureView)
+ ? activeFramebuffer.aaDepthTexture
+ ? activeFramebuffer.aaDepthTextureView
+ : activeFramebuffer.depthTextureView
: this.depthTextureView;
const renderPassDescriptor = {
colorAttachments: [colorAttachment],
depthStencilAttachment: depthTextureView
? {
view: depthTextureView,
- depthLoadOp: "load",
- depthStoreOp: "store",
+ depthLoadOp: 'load',
+ depthStoreOp: 'store',
depthClearValue: 1.0,
- stencilLoadOp: "load",
- stencilStoreOp: "store",
+ stencilLoadOp: 'load',
+ stencilStoreOp: 'store',
depthReadOnly: false,
- stencilReadOnly: false,
+ stencilReadOnly: false
}
- : undefined,
+ : undefined
};
const commandEncoder = this.device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
@@ -635,7 +663,10 @@ function rendererWebGPU(p5, fn) {
// Note: a mid-draw-loop transition from UNPROMOTED back to PROMOTED
// (i.e. calling background() some frames but not others) will still
// lose intermediate UNPROMOTED frame content.
- if (this._frameState !== FRAME_STATE.PROMOTED && !this.activeFramebuffer()) {
+ if (
+ this._frameState !== FRAME_STATE.PROMOTED &&
+ !this.activeFramebuffer()
+ ) {
if (this._pInst.frameCount > 0) {
this._frameState = FRAME_STATE.UNPROMOTED;
} else {
@@ -653,39 +684,40 @@ function rendererWebGPU(p5, fn) {
const colorAttachment = {
view: activeFramebuffer
- ? (activeFramebuffer.aaColorTexture
- ? activeFramebuffer.aaColorTextureView
- : activeFramebuffer.colorTextureView)
+ ? activeFramebuffer.aaColorTexture
+ ? activeFramebuffer.aaColorTextureView
+ : activeFramebuffer.colorTextureView
: this._getCanvasColorTextureView(),
clearValue: { r: _r * _a, g: _g * _a, b: _b * _a, a: _a },
loadOp: 'clear',
storeOp: 'store',
// If using multisampled texture, resolve to non-multisampled texture
- resolveTarget: activeFramebuffer && activeFramebuffer.aaColorTexture
- ? activeFramebuffer.colorTextureView
- : undefined,
+ resolveTarget:
+ activeFramebuffer && activeFramebuffer.aaColorTexture
+ ? activeFramebuffer.colorTextureView
+ : undefined
};
// Use framebuffer depth texture if active, otherwise use canvas depth texture
const depthTextureView = activeFramebuffer
- ? (activeFramebuffer.aaDepthTexture
- ? activeFramebuffer.aaDepthTextureView
- : activeFramebuffer.depthTextureView)
+ ? activeFramebuffer.aaDepthTexture
+ ? activeFramebuffer.aaDepthTextureView
+ : activeFramebuffer.depthTextureView
: this.depthTextureView;
const depthAttachment = depthTextureView
? {
- view: depthTextureView,
- depthClearValue: 1.0,
- depthLoadOp: 'clear',
- depthStoreOp: 'store',
- stencilLoadOp: 'load',
- stencilStoreOp: 'store',
- }
+ view: depthTextureView,
+ depthClearValue: 1.0,
+ depthLoadOp: 'clear',
+ depthStoreOp: 'store',
+ stencilLoadOp: 'load',
+ stencilStoreOp: 'store'
+ }
: undefined;
const renderPassDescriptor = {
colorAttachments: [colorAttachment],
- ...(depthAttachment ? { depthStencilAttachment: depthAttachment } : {}),
+ ...(depthAttachment ? { depthStencilAttachment: depthAttachment } : {})
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
@@ -709,9 +741,9 @@ function rendererWebGPU(p5, fn) {
// Use framebuffer depth texture if active, otherwise use canvas depth texture
const depthTextureView = activeFramebuffer
- ? (activeFramebuffer.aaDepthTexture
- ? activeFramebuffer.aaDepthTextureView
- : activeFramebuffer.depthTextureView)
+ ? activeFramebuffer.aaDepthTexture
+ ? activeFramebuffer.aaDepthTextureView
+ : activeFramebuffer.depthTextureView
: this.depthTextureView;
if (!depthTextureView) {
@@ -725,12 +757,12 @@ function rendererWebGPU(p5, fn) {
depthLoadOp: 'clear',
depthStoreOp: 'store',
stencilLoadOp: 'load',
- stencilStoreOp: 'store',
+ stencilStoreOp: 'store'
};
const renderPassDescriptor = {
colorAttachments: [], // No color attachments, we're only clearing depth
- depthStencilAttachment: depthAttachment,
+ depthStencilAttachment: depthAttachment
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
@@ -768,7 +800,11 @@ function rendererWebGPU(p5, fn) {
const typed = this._normalizeBufferData(raw, Float32Array);
// Get pooled buffer (may reuse existing or create new)
- const pooledBufferInfo = this._getVertexBufferFromPool(geometry, dst, typed.byteLength);
+ const pooledBufferInfo = this._getVertexBufferFromPool(
+ geometry,
+ dst,
+ typed.byteLength
+ );
// Create a copy of the data to avoid conflicts when geometry arrays are reset
const dataCopy = new typed.constructor(typed);
@@ -804,7 +840,7 @@ function rendererWebGPU(p5, fn) {
const buffer = device.createBuffer({
size: Math.ceil((indices.length * indexType.BYTES_PER_ELEMENT) / 4) * 4,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
- mappedAtCreation: true,
+ mappedAtCreation: true
});
// Write index data to buffer
@@ -817,7 +853,7 @@ function rendererWebGPU(p5, fn) {
}
_freeBuffers(buffers) {
- const destroyIfExists = (buf) => {
+ const destroyIfExists = buf => {
if (buf && buf.destroy) {
buf.destroy();
}
@@ -825,7 +861,7 @@ function rendererWebGPU(p5, fn) {
destroyIfExists(buffers.indexBuffer);
- const freeDefs = (defs) => {
+ const freeDefs = defs => {
for (const def of defs) {
destroyIfExists(buffers[def.dst]);
buffers[def.dst] = null;
@@ -847,17 +883,21 @@ function rendererWebGPU(p5, fn) {
_shaderOptions({ mode, compute, workgroupSize }) {
if (compute) return { compute: true, workgroupSize };
const activeFramebuffer = this.activeFramebuffer();
- const format = activeFramebuffer ?
- this._getWebGPUColorFormat(activeFramebuffer) :
- this.presentationFormat;
-
- const requestedSampleCount = activeFramebuffer ?
- (activeFramebuffer.antialias ? activeFramebuffer.antialiasSamples : 1) :
- 1; // No MSAA needed when blitting already-antialiased textures to canvas
+ const format = activeFramebuffer
+ ? this._getWebGPUColorFormat(activeFramebuffer)
+ : this.presentationFormat;
+
+ const requestedSampleCount = activeFramebuffer
+ ? activeFramebuffer.antialias
+ ? activeFramebuffer.antialiasSamples
+ : 1
+ : 1; // No MSAA needed when blitting already-antialiased textures to canvas
const sampleCount = this._getValidSampleCount(requestedSampleCount);
const depthFormat = activeFramebuffer
- ? (activeFramebuffer.useDepth ? this._getWebGPUDepthFormat(activeFramebuffer) : undefined)
+ ? activeFramebuffer.useDepth
+ ? this._getWebGPUDepthFormat(activeFramebuffer)
+ : undefined
: this.depthFormat;
const drawTarget = this.drawTarget();
@@ -865,14 +905,17 @@ function rendererWebGPU(p5, fn) {
const clipApplied = drawTarget._isClipApplied;
return {
- topology: mode === constants.TRIANGLE_STRIP ? 'triangle-strip' : 'triangle-list',
+ topology:
+ mode === constants.TRIANGLE_STRIP
+ ? 'triangle-strip'
+ : 'triangle-list',
blendMode: this.states.curBlendMode,
sampleCount,
format,
depthFormat,
clipping,
- clipApplied,
- }
+ clipApplied
+ };
}
_shaderOptionsDifferent(newOptions) {
@@ -888,7 +931,9 @@ function rendererWebGPU(p5, fn) {
if (shader.shaderType === 'compute') {
// Compute shader initialization
- shader.computeModule = device.createShaderModule({ code: shader.computeSrc() });
+ shader.computeModule = device.createShaderModule({
+ code: shader.computeSrc()
+ });
shader._computePipelineCache = null;
shader._workgroupSize = null;
@@ -915,7 +960,15 @@ function rendererWebGPU(p5, fn) {
shader.fragModule = device.createShaderModule({ code: shader.fragSrc() });
shader._pipelineCache = new Map();
- shader.getPipeline = ({ topology, blendMode, sampleCount, format, depthFormat, clipping, clipApplied }) => {
+ shader.getPipeline = ({
+ topology,
+ blendMode,
+ sampleCount,
+ format,
+ depthFormat,
+ clipping,
+ clipApplied
+ }) => {
const key = `${topology}_${blendMode}_${sampleCount}_${format}_${depthFormat}_${clipping}_${clipApplied}`;
if (!shader._pipelineCache.has(key)) {
const pipeline = device.createRenderPipeline({
@@ -923,44 +976,56 @@ function rendererWebGPU(p5, fn) {
vertex: {
module: shader.vertModule,
entryPoint: 'main',
- buffers: this._getVertexLayout(shader),
+ buffers: this._getVertexLayout(shader)
},
fragment: {
module: shader.fragModule,
entryPoint: 'main',
- targets: [{
- format,
- blend: this._getBlendState(blendMode),
- }],
+ targets: [
+ {
+ format,
+ blend: this._getBlendState(blendMode)
+ }
+ ]
},
primitive: { topology },
multisample: { count: sampleCount },
- ...(depthFormat ? {
- depthStencil: {
- format: depthFormat,
- depthWriteEnabled: !clipping,
- depthCompare: 'less-equal',
- stencilFront: {
- compare: clipping ? 'always' : (clipApplied ? 'not-equal' : 'always'),
- failOp: 'keep',
- depthFailOp: 'keep',
- passOp: clipping ? 'replace' : 'keep',
- },
- stencilBack: {
- compare: clipping ? 'always' : (clipApplied ? 'not-equal' : 'always'),
- failOp: 'keep',
- depthFailOp: 'keep',
- passOp: clipping ? 'replace' : 'keep',
- },
- stencilReadMask: 0xFF,
- stencilWriteMask: clipping ? 0xFF : 0x00,
- },
- } : {}),
+ ...(depthFormat
+ ? {
+ depthStencil: {
+ format: depthFormat,
+ depthWriteEnabled: !clipping,
+ depthCompare: 'less-equal',
+ stencilFront: {
+ compare: clipping
+ ? 'always'
+ : clipApplied
+ ? 'not-equal'
+ : 'always',
+ failOp: 'keep',
+ depthFailOp: 'keep',
+ passOp: clipping ? 'replace' : 'keep'
+ },
+ stencilBack: {
+ compare: clipping
+ ? 'always'
+ : clipApplied
+ ? 'not-equal'
+ : 'always',
+ failOp: 'keep',
+ depthFailOp: 'keep',
+ passOp: clipping ? 'replace' : 'keep'
+ },
+ stencilReadMask: 0xff,
+ stencilWriteMask: clipping ? 0xff : 0x00
+ }
+ }
+ : {})
});
shader._pipelineCache.set(key, pipeline);
}
return shader._pipelineCache.get(key);
- }
+ };
}
_finalizeShader(shader) {
@@ -974,10 +1039,7 @@ function rendererWebGPU(p5, fn) {
for (const group of shader._uniformGroups) {
// Calculate the size needed for this group's uniforms
const groupUniforms = Object.values(group.uniforms);
- const rawSize = Math.max(
- 0,
- ...groupUniforms.map(u => u.offsetEnd)
- );
+ const rawSize = Math.max(0, ...groupUniforms.map(u => u.offsetEnd));
const alignedSize = Math.ceil(rawSize / 16) * 16;
shader._uniformBufferGroups.push({
@@ -994,7 +1056,7 @@ function rendererWebGPU(p5, fn) {
dynamic: groupUniforms.some(u => u.name.startsWith('uModel')),
buffersInUse: new Set(),
- currentBuffer: null, // For caching
+ currentBuffer: null // For caching
});
}
@@ -1011,10 +1073,11 @@ function rendererWebGPU(p5, fn) {
entries.push({
bufferGroup,
binding: bufferGroup.binding,
- visibility: shader.shaderType === 'compute'
- ? GPUShaderStage.COMPUTE
- : GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
- buffer: { type: 'uniform', hasDynamicOffset: bufferGroup.dynamic },
+ visibility:
+ shader.shaderType === 'compute'
+ ? GPUShaderStage.COMPUTE
+ : GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
+ buffer: { type: 'uniform', hasDynamicOffset: bufferGroup.dynamic }
});
structEntries.set(bufferGroup.group, entries);
}
@@ -1034,13 +1097,13 @@ function rendererWebGPU(p5, fn) {
entries.push({
binding: sampler.binding,
visibility: sampler.visibility,
- sampler: sampler.type === 'sampler'
- ? { type: 'filtering' }
- : undefined,
- texture: sampler.type === 'texture_2d'
- ? { sampleType: 'float', viewDimension: '2d' }
- : undefined,
- uniform: sampler,
+ sampler:
+ sampler.type === 'sampler' ? { type: 'filtering' } : undefined,
+ texture:
+ sampler.type === 'texture_2d'
+ ? { sampleType: 'float', viewDimension: '2d' }
+ : undefined,
+ uniform: sampler
});
entries.sort((a, b) => a.binding - b.binding);
@@ -1056,9 +1119,10 @@ function rendererWebGPU(p5, fn) {
binding: storage.binding,
visibility: storage.visibility,
buffer: {
- type: storage.accessMode === 'read' ? 'read-only-storage' : 'storage'
+ type:
+ storage.accessMode === 'read' ? 'read-only-storage' : 'storage'
},
- storage: storage,
+ storage: storage
});
entries.sort((a, b) => a.binding - b.binding);
@@ -1081,7 +1145,7 @@ function rendererWebGPU(p5, fn) {
// possibly cache bind groups if unchanged
shader._lastDynamicBuffer = {};
shader._pipelineLayout = this.device.createPipelineLayout({
- bindGroupLayouts: shader._bindGroupLayouts,
+ bindGroupLayouts: shader._bindGroupLayouts
});
shader._compiled = true;
}
@@ -1260,9 +1324,9 @@ function rendererWebGPU(p5, fn) {
{
shaderLocation: attr.location,
offset: 0,
- format,
- },
- ],
+ format
+ }
+ ]
});
}
return layouts;
@@ -1278,8 +1342,9 @@ function rendererWebGPU(p5, fn) {
// Get the vertex buffer info associated with this attribute
const renderBuffer =
- this.buffers[shader.shaderType].find(buf => buf.attr === attrName) ||
- this.buffers.user.find(buf => buf.attr === attrName);
+ this.buffers[shader.shaderType].find(
+ buf => buf.attr === attrName
+ ) || this.buffers.user.find(buf => buf.attr === attrName);
if (!renderBuffer) continue;
buffers.push(renderBuffer);
@@ -1292,11 +1357,16 @@ function rendererWebGPU(p5, fn) {
_getFormatFromSize(size) {
switch (size) {
- case 1: return 'float32';
- case 2: return 'float32x2';
- case 3: return 'float32x3';
- case 4: return 'float32x4';
- default: throw new Error(`Unsupported attribute size: ${size}`);
+ case 1:
+ return 'float32';
+ case 2:
+ return 'float32x2';
+ case 3:
+ return 'float32x3';
+ case 4:
+ return 'float32x4';
+ default:
+ throw new Error(`Unsupported attribute size: ${size}`);
}
}
@@ -1305,7 +1375,7 @@ function rendererWebGPU(p5, fn) {
_updateViewport() {
this._origViewport = {
width: this.width,
- height: this.height,
+ height: this.height
};
this._viewport = [0, 0, this.width, this.height];
}
@@ -1345,9 +1415,9 @@ function rendererWebGPU(p5, fn) {
const commandEncoder = this.device.createCommandEncoder();
const depthTextureView = activeFramebuffer
- ? (activeFramebuffer.aaDepthTexture
- ? activeFramebuffer.aaDepthTextureView
- : activeFramebuffer.depthTextureView)
+ ? activeFramebuffer.aaDepthTexture
+ ? activeFramebuffer.aaDepthTextureView
+ : activeFramebuffer.depthTextureView
: this.depthTextureView;
if (depthTextureView) {
@@ -1357,13 +1427,14 @@ function rendererWebGPU(p5, fn) {
depthLoadOp: 'clear',
depthStoreOp: 'store',
stencilLoadOp: 'load',
- stencilStoreOp: 'store',
+ stencilStoreOp: 'store'
};
const renderPassDescriptor = {
colorAttachments: [],
- depthStencilAttachment: depthAttachment,
+ depthStencilAttachment: depthAttachment
};
- const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+ const passEncoder =
+ commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.end();
this._pendingCommandEncoders.push(commandEncoder.finish());
this._hasPendingDraws = true;
@@ -1396,8 +1467,10 @@ function rendererWebGPU(p5, fn) {
const canvasTexture = this.drawingContext.getCurrentTexture();
// Ensure mainFramebuffer matches canvas size
- if (this.mainFramebuffer.width !== this.width ||
- this.mainFramebuffer.height !== this.height) {
+ if (
+ this.mainFramebuffer.width !== this.width ||
+ this.mainFramebuffer.height !== this.height
+ ) {
this.mainFramebuffer.resize(this.width, this.height);
}
@@ -1409,17 +1482,17 @@ function rendererWebGPU(p5, fn) {
{
texture: canvasTexture,
origin: { x: 0, y: 0, z: 0 },
- mipLevel: 0,
+ mipLevel: 0
},
{
texture: this.mainFramebuffer.colorTexture,
origin: { x: 0, y: 0, z: 0 },
- mipLevel: 0,
+ mipLevel: 0
},
{
width: Math.ceil(this.width * this._pixelDensity),
height: Math.ceil(this.height * this._pixelDensity),
- depthOrArrayLayers: 1,
+ depthOrArrayLayers: 1
}
);
@@ -1428,17 +1501,17 @@ function rendererWebGPU(p5, fn) {
{
texture: this.depthTexture,
origin: { x: 0, y: 0, z: 0 },
- mipLevel: 0,
+ mipLevel: 0
},
{
texture: this.mainFramebuffer.depthTexture,
origin: { x: 0, y: 0, z: 0 },
- mipLevel: 0,
+ mipLevel: 0
},
{
width: Math.ceil(this.width * this._pixelDensity),
height: Math.ceil(this.height * this._pixelDensity),
- depthOrArrayLayers: 1,
+ depthOrArrayLayers: 1
}
);
@@ -1465,8 +1538,10 @@ function rendererWebGPU(p5, fn) {
}
// Ensure mainFramebuffer matches canvas size
- if (this.mainFramebuffer.width !== this.width ||
- this.mainFramebuffer.height !== this.height) {
+ if (
+ this.mainFramebuffer.width !== this.width ||
+ this.mainFramebuffer.height !== this.height
+ ) {
this.mainFramebuffer.resize(this.width, this.height);
}
@@ -1538,7 +1613,7 @@ function rendererWebGPU(p5, fn) {
// No suitable buffer available, create a new one
const newBuffer = this.device.createBuffer({
size,
- usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
const bufferInfo = {
@@ -1556,7 +1631,9 @@ function rendererWebGPU(p5, fn) {
// Return buffers marked for return back to their pools for all registered geometries
for (const geometry of this._geometriesWithPools) {
if (geometry._vertexBuffersToReturn) {
- for (const [dst, buffersToReturn] of Object.entries(geometry._vertexBuffersToReturn)) {
+ for (const [dst, buffersToReturn] of Object.entries(
+ geometry._vertexBuffersToReturn
+ )) {
if (buffersToReturn.length > 0) {
// Move all buffers from ToReturn back to pool
const pool = geometry._vertexBufferPools[dst] || [];
@@ -1581,7 +1658,9 @@ function rendererWebGPU(p5, fn) {
// Mark geometry buffers for return when geometry is reset/freed
_markGeometryBuffersForReturn(geometry) {
if (geometry._vertexBuffersInUse && geometry._vertexBuffersToReturn) {
- for (const [dst, buffersInUse] of Object.entries(geometry._vertexBuffersInUse)) {
+ for (const [dst, buffersInUse] of Object.entries(
+ geometry._vertexBuffersInUse
+ )) {
if (buffersInUse.length > 0) {
// Move all buffers from InUse to ToReturn
const buffersToReturn = geometry._vertexBuffersToReturn[dst] || [];
@@ -1610,7 +1689,7 @@ function rendererWebGPU(p5, fn) {
// No buffers available, create a new one
const newBuffer = this.device.createBuffer({
size: bufferGroup.size,
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});
const newData = new Float32Array(bufferGroup.size / 4);
const newDataView = new DataView(newData.buffer);
@@ -1629,7 +1708,8 @@ function rendererWebGPU(p5, fn) {
let buffer;
if (
this.currentUniformBuffer &&
- this.currentUniformBuffer.offset + bufferGroup.size < this.currentUniformBuffer.size
+ this.currentUniformBuffer.offset + bufferGroup.size <
+ this.currentUniformBuffer.size
) {
// We can fit this next block of uniforms into the current active memory chunk
buffer = this.currentUniformBuffer;
@@ -1652,13 +1732,13 @@ function rendererWebGPU(p5, fn) {
buffer: this.device.createBuffer({
size,
usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
- mappedAtCreation: true,
+ mappedAtCreation: true
}),
uniformBuffer: this.device.createBuffer({
size,
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
- }),
- }
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
+ })
+ };
buffer.data = new Float32Array(buffer.buffer.getMappedRange());
buffer.dataView = new DataView(buffer.data.buffer);
@@ -1712,7 +1792,7 @@ function rendererWebGPU(p5, fn) {
bufferInfo.buffer.unmap();
encoder.copyBufferToBuffer(
bufferInfo.buffer,
- bufferInfo.uniformBuffer,
+ bufferInfo.uniformBuffer
);
}
commandsToSubmit.unshift(encoder.finish());
@@ -1723,19 +1803,19 @@ function rendererWebGPU(p5, fn) {
for (const buf of this.activeUniformBuffers) {
// buf.buffer = this.device.createBuffer({
- // size: buf.size,
- // usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
- // mappedAtCreation: true,
+ // size: buf.size,
+ // usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
+ // mappedAtCreation: true,
// });
buf.offset = 0;
buf.lastOffset = 0;
// this.resettingUniformBuffers.push(
- buf.buffer.mapAsync(GPUMapMode.WRITE).then(() => {
- buf.data = new Float32Array(buf.buffer.getMappedRange());
- buf.dataView = new DataView(buf.data.buffer);
- this.uniformBufferPool.push(buf);
- return buf;
- })
+ buf.buffer.mapAsync(GPUMapMode.WRITE).then(() => {
+ buf.data = new Float32Array(buf.buffer.getMappedRange());
+ buf.dataView = new DataView(buf.data.buffer);
+ this.uniformBufferPool.push(buf);
+ return buf;
+ });
// )
}
this.activeUniformBuffers = [];
@@ -1766,7 +1846,10 @@ function rendererWebGPU(p5, fn) {
}
// Resize canvas if dimensions changed
- if (this._pixelReadCanvas.width !== width || this._pixelReadCanvas.height !== height) {
+ if (
+ this._pixelReadCanvas.width !== width ||
+ this._pixelReadCanvas.height !== height
+ ) {
this._pixelReadCanvas.width = width;
this._pixelReadCanvas.height = height;
}
@@ -1832,7 +1915,10 @@ function rendererWebGPU(p5, fn) {
if (this._frameState === FRAME_STATE.PROMOTED) {
for (const { fbo, diff } of states) {
- if (fbo !== this.mainFramebuffer || this._frameState !== FRAME_STATE.PROMOTED) {
+ if (
+ fbo !== this.mainFramebuffer ||
+ this._frameState !== FRAME_STATE.PROMOTED
+ ) {
fbo.begin();
}
for (const key in diff) {
@@ -1851,7 +1937,10 @@ function rendererWebGPU(p5, fn) {
if (!buffers) return;
// If PENDING and no custom framebuffer, regular draw means PROMOTE
- if (this._frameState === FRAME_STATE.PENDING && !this.activeFramebuffer()) {
+ if (
+ this._frameState === FRAME_STATE.PENDING &&
+ !this.activeFramebuffer()
+ ) {
this._promoteToFramebufferWithoutCopy();
}
@@ -1861,31 +1950,32 @@ function rendererWebGPU(p5, fn) {
const currentShader = this._curShader;
this.setupShaderBindGroups(currentShader, passEncoder, { mode, buffers });
// Bind vertex buffers
- for (const buffer of currentShader._vertexBuffers || this._getVertexBuffers(currentShader)) {
+ for (const buffer of currentShader._vertexBuffers ||
+ this._getVertexBuffers(currentShader)) {
const location = currentShader.attributes[buffer.attr].location;
const gpuBuffer = buffers[buffer.dst];
passEncoder.setVertexBuffer(location, gpuBuffer, 0);
}
- if (currentShader.shaderType === "fill") {
+ if (currentShader.shaderType === 'fill') {
// Bind index buffer and issue draw
if (buffers.indexBuffer) {
- const indexFormat = buffers.indexFormat || "uint16";
+ const indexFormat = buffers.indexFormat || 'uint16';
passEncoder.setIndexBuffer(buffers.indexBuffer, indexFormat);
passEncoder.drawIndexed(geometry.faces.length * 3, count, 0, 0, 0);
} else {
passEncoder.draw(geometry.vertices.length, count, 0, 0);
}
- } else if (currentShader.shaderType === "text") {
+ } else if (currentShader.shaderType === 'text') {
if (!buffers.indexBuffer) {
- throw new Error("Text geometry must have an index buffer");
+ throw new Error('Text geometry must have an index buffer');
}
- const indexFormat = buffers.indexFormat || "uint16";
+ const indexFormat = buffers.indexFormat || 'uint16';
passEncoder.setIndexBuffer(buffers.indexBuffer, indexFormat);
passEncoder.drawIndexed(geometry.faces.length * 3, count, 0, 0, 0);
}
- if (buffers.lineVerticesBuffer && currentShader.shaderType === "stroke") {
+ if (buffers.lineVerticesBuffer && currentShader.shaderType === 'stroke') {
passEncoder.draw(geometry.lineVertices.length / 3, count, 0, 0);
}
@@ -1923,14 +2013,25 @@ function rendererWebGPU(p5, fn) {
if (bufferGroup.dynamic) {
// Bind uniforms into a part of a big dynamic memory block because
// the group changes often
- const uniformBufferInfo = this._getDynamicUniformBufferFromPool(bufferGroup);
- if (currentShader._lastDynamicBuffer[bufferGroup.cacheKey] !== uniformBufferInfo) {
+ const uniformBufferInfo =
+ this._getDynamicUniformBufferFromPool(bufferGroup);
+ if (
+ currentShader._lastDynamicBuffer[bufferGroup.cacheKey] !==
+ uniformBufferInfo
+ ) {
currentShader._cachedBindGroup[bufferGroup.group] = undefined;
- currentShader._lastDynamicBuffer[bufferGroup.cacheKey] = uniformBufferInfo;
+ currentShader._lastDynamicBuffer[bufferGroup.cacheKey] =
+ uniformBufferInfo;
}
- this._packUniformGroup(currentShader, bufferGroup.uniforms, uniformBufferInfo);
+ this._packUniformGroup(
+ currentShader,
+ bufferGroup.uniforms,
+ uniformBufferInfo
+ );
uniformBufferInfo.lastOffset = uniformBufferInfo.offset;
- uniformBufferInfo.offset += Math.ceil(bufferGroup.size / this.uniformBufferAlignment) * this.uniformBufferAlignment;
+ uniformBufferInfo.offset +=
+ Math.ceil(bufferGroup.size / this.uniformBufferAlignment) *
+ this.uniformBufferAlignment;
// Make a shallow copy so that we keep track of the last offset for this uniform
bufferGroup.currentDynamicBuffer = uniformBufferInfo;
@@ -1938,7 +2039,10 @@ function rendererWebGPU(p5, fn) {
} else {
// Bind uniforms to a binding-specific buffer, which may be cached for performance
let bufferInfo;
- const dataChanged = this._hasGroupDataChanged(currentShader, bufferGroup);
+ const dataChanged = this._hasGroupDataChanged(
+ currentShader,
+ bufferGroup
+ );
if (!dataChanged && bufferGroup.currentBuffer) {
// Reuse the cached buffer - no need to pack or write
@@ -1947,7 +2051,11 @@ function rendererWebGPU(p5, fn) {
} else {
// Data changed - get a new buffer and write to it
bufferInfo = this._getUniformBufferFromPool(bufferGroup);
- this._packUniformGroup(currentShader, bufferGroup.uniforms, bufferInfo);
+ this._packUniformGroup(
+ currentShader,
+ bufferGroup.uniforms,
+ bufferInfo
+ );
this.device.queue.writeBuffer(
bufferInfo.buffer,
0,
@@ -1956,7 +2064,9 @@ function rendererWebGPU(p5, fn) {
bufferInfo.data.byteLength
);
- currentShader.buffersDirty.delete(bufferGroup.group * 1000 + bufferGroup.binding);
+ currentShader.buffersDirty.delete(
+ bufferGroup.group * 1000 + bufferGroup.binding
+ );
currentShader._cachedBindGroup[bufferGroup.group] = undefined;
// Cache this buffer and data for next frame
@@ -1993,39 +2103,52 @@ function rendererWebGPU(p5, fn) {
bufferGroup?.currentBuffer || bufferGroup?.currentDynamicBuffer;
if (uniformBufferInfo) {
if (bufferGroup.dynamic) {
- this.dynamicEntryOffsets[dynamicOffsetIdx++] = bufferGroup.lastOffset;
+ this.dynamicEntryOffsets[dynamicOffsetIdx++] =
+ bufferGroup.lastOffset;
}
if (!bindGroup) {
bgEntries.push({
binding: entry.binding,
resource: bufferGroup.dynamic
? {
- buffer: uniformBufferInfo.uniformBuffer,
- offset: 0,
- size: Math.ceil(bufferGroup.size / this.uniformBufferAlignment) * this.uniformBufferAlignment,
- }
- : { buffer: uniformBufferInfo.buffer },
+ buffer: uniformBufferInfo.uniformBuffer,
+ offset: 0,
+ size:
+ Math.ceil(
+ bufferGroup.size / this.uniformBufferAlignment
+ ) * this.uniformBufferAlignment
+ }
+ : { buffer: uniformBufferInfo.buffer }
});
}
} else if (entry.storage && !bindGroup) {
// Storage buffer binding
const uniform = currentShader.uniforms[entry.storage.name];
- if (!uniform || !uniform._cachedData || !uniform._cachedData._isStorageBuffer) {
+ if (
+ !uniform ||
+ !uniform._cachedData ||
+ !uniform._cachedData._isStorageBuffer
+ ) {
throw new Error(
`Storage buffer "${entry.storage.name}" not set. ` +
- `Use shader.setUniform("${entry.storage.name}", storageBuffer)`
+ `Use shader.setUniform("${entry.storage.name}", storageBuffer)`
);
}
bgEntries.push({
binding: entry.binding,
- resource: { buffer: uniform._cachedData.buffer },
+ resource: { buffer: uniform._cachedData.buffer }
});
} else if (!bindGroup) {
bgEntries.push({
binding: entry.binding,
- resource: entry.uniform.type === 'sampler'
- ? (entry.uniform.textureSource.texture || this._getEmptyTexture()).getSampler()
- : (entry.uniform.texture || this._getEmptyTexture()).textureHandle.view,
+ resource:
+ entry.uniform.type === 'sampler'
+ ? (
+ entry.uniform.textureSource.texture ||
+ this._getEmptyTexture()
+ ).getSampler()
+ : (entry.uniform.texture || this._getEmptyTexture())
+ .textureHandle.view
});
}
}
@@ -2034,15 +2157,12 @@ function rendererWebGPU(p5, fn) {
if (!bindGroup) {
bindGroup = this.device.createBindGroup({
layout,
- entries: bgEntries,
+ entries: bgEntries
});
}
currentShader._cachedBindGroup[group] = bindGroup;
if (dynamicOffsetIdx === 0) {
- passEncoder.setBindGroup(
- group,
- bindGroup,
- );
+ passEncoder.setBindGroup(group, bindGroup);
} else {
passEncoder.setBindGroup(
group,
@@ -2075,7 +2195,10 @@ function rendererWebGPU(p5, fn) {
// Duck typing instead of instanceof to avoid importing a separate
// copy of the Color/Vector classes
if (value?.isVector) {
- value = value.values.length !== value.dimensions ? value.values.slice(0, value.dimensions) : value.values;
+ value =
+ value.values.length !== value.dimensions
+ ? value.values.slice(0, value.dimensions)
+ : value.values;
} else if (value?.isColor) {
value = value._getRGBA([1, 1, 1, 1]);
}
@@ -2099,9 +2222,15 @@ function rendererWebGPU(p5, fn) {
} else if (field.packInPlace) {
// In-place packing for mat3: write directly to buffer with padding
const base = byteOffset / 4;
- floatView[base + 0] = value[0]; floatView[base + 1] = value[1]; floatView[base + 2] = value[2];
- floatView[base + 4] = value[3]; floatView[base + 5] = value[4]; floatView[base + 6] = value[5];
- floatView[base + 8] = value[6]; floatView[base + 9] = value[7]; floatView[base + 10] = value[8];
+ floatView[base + 0] = value[0];
+ floatView[base + 1] = value[1];
+ floatView[base + 2] = value[2];
+ floatView[base + 4] = value[3];
+ floatView[base + 5] = value[4];
+ floatView[base + 6] = value[5];
+ floatView[base + 8] = value[6];
+ floatView[base + 9] = value[7];
+ floatView[base + 10] = value[8];
} else if (field.size === 4) {
floatView.set([value], byteOffset / 4);
} else {
@@ -2117,14 +2246,22 @@ function rendererWebGPU(p5, fn) {
for (const uniform of groupUniforms) {
const fullUniform = shader.uniforms[uniform.name];
if (!fullUniform || fullUniform.isSampler) continue;
- this._packField(fullUniform, fullUniform._mappedData, data, dataView, offset);
+ this._packField(
+ fullUniform,
+ fullUniform._mappedData,
+ data,
+ dataView,
+ offset
+ );
}
}
_hasGroupDataChanged(shader, bufferGroup) {
// First time
if (!bufferGroup.currentBuffer) return true;
- return shader.buffersDirty.has(bufferGroup.group * 1000 + bufferGroup.binding);
+ return shader.buffersDirty.has(
+ bufferGroup.group * 1000 + bufferGroup.binding
+ );
}
_parseStruct(shaderSource, structName) {
@@ -2141,10 +2278,9 @@ function rendererWebGPU(p5, fn) {
let index = 0;
let offset = 0;
- const elementRegex =
- /(?:@location\((\d+)\)\s+)?(\w+):\s*([^\n]+?),?\n/g
+ const elementRegex = /(?:@location\((\d+)\)\s+)?(\w+):\s*([^\n]+?),?\n/g;
- const baseAlignAndSize = (type) => {
+ const baseAlignAndSize = type => {
if (['f32', 'i32', 'u32', 'bool'].includes(type)) {
return { align: 4, size: 4, items: 1, baseType: type };
}
@@ -2169,17 +2305,25 @@ function rendererWebGPU(p5, fn) {
const dim = parseInt(type[3]);
const align = dim === 2 ? 8 : 16;
// Each column must be aligned
- const size = Math.ceil(dim * 4 / align) * align * dim;
+ const size = Math.ceil((dim * 4) / align) * align * dim;
// For mat3, use in-place packing to avoid array allocation
- const pack = dim === 3
- ? (data) => [
- ...data.slice(0, 3),
- ...data.slice(3, 6),
- ...data.slice(6, 9),
- ]
- : undefined;
+ const pack =
+ dim === 3
+ ? data => [
+ ...data.slice(0, 3),
+ ...data.slice(3, 6),
+ ...data.slice(6, 9)
+ ]
+ : undefined;
const packInPlace = dim === 3;
- return { align, size, pack, packInPlace, items: dim * dim, baseType: 'f32' };
+ return {
+ align,
+ size,
+ pack,
+ packInPlace,
+ items: dim * dim,
+ baseType: 'f32'
+ };
}
if (/^array<.+>$/.test(type)) {
const [, subtype, rawLength] = type.match(/^array<(.+),\s*(\d+)>/);
@@ -2188,14 +2332,14 @@ function rendererWebGPU(p5, fn) {
align: elemAlign,
size: elemSize,
items: elemItems,
- pack: elemPack = (data) => [...data],
+ pack: elemPack = data => [...data],
baseType: elemBaseType
} = baseAlignAndSize(subtype);
const stride = Math.ceil(elemSize / elemAlign) * elemAlign;
- const pack = (data) => {
+ const pack = data => {
const result = [];
for (let i = 0; i < data.length; i += elemItems) {
- const elemData = elemPack(data.slice(i, elemItems))
+ const elemData = elemPack(data.slice(i, elemItems));
result.push(...elemData);
for (let j = 0; j < stride / 4 - elemData.length; j++) {
result.push(0);
@@ -2216,7 +2360,8 @@ function rendererWebGPU(p5, fn) {
while ((match = elementRegex.exec(structBody)) !== null) {
const [_, location, name, type] = match;
- const { size, align, pack, packInPlace, baseType } = baseAlignAndSize(type);
+ const { size, align, pack, packInPlace, baseType } =
+ baseAlignAndSize(type);
offset = Math.ceil(offset / align) * align;
const offsetEnd = offset + size;
elements[name] = {
@@ -2246,8 +2391,9 @@ function rendererWebGPU(p5, fn) {
}
_getShaderAttributes(shader) {
- const mainMatch = /fn main\(.+:\s*([^\s\)]+)/.exec(shader._vertSrc);
- if (!mainMatch) throw new Error("Can't find `fn main` in vertex shader source");
+ const mainMatch = /fn main\(.+:\s*([^\s)]+)/.exec(shader._vertSrc);
+ if (!mainMatch)
+ throw new Error("Can't find `fn main` in vertex shader source");
const inputType = mainMatch[1];
return this._parseStruct(shader.vertSrc(), inputType);
@@ -2261,10 +2407,14 @@ function rendererWebGPU(p5, fn) {
// updated or cached all at once.
const uniformGroups = [];
- const uniformVarRegex = /@group\((\d+)\)\s+@binding\((\d+)\)\s+var\s+(\w+)\s*:\s*(\w+);/g;
+ const uniformVarRegex =
+ /@group\((\d+)\)\s+@binding\((\d+)\)\s+var\s+(\w+)\s*:\s*(\w+);/g;
let match;
- const src = shader.shaderType === 'compute' ? shader.computeSrc() : shader.vertSrc();
+ const src =
+ shader.shaderType === 'compute'
+ ? shader.computeSrc()
+ : shader.vertSrc();
while ((match = uniformVarRegex.exec(src)) !== null) {
const [_, groupNum, binding, varName, structType] = match;
const bindingIndex = parseInt(binding);
@@ -2280,7 +2430,9 @@ function rendererWebGPU(p5, fn) {
}
if (uniformGroups.length === 0 && shader.shaderType !== 'compute') {
- throw new Error('Expected at least one uniform struct bound to @group(0)');
+ throw new Error(
+ 'Expected at least one uniform struct bound to @group(0)'
+ );
}
// While we're also keeping track of the groups, the API we expose
@@ -2288,7 +2440,9 @@ function rendererWebGPU(p5, fn) {
// individual struct items in the group.)
const allUniforms = {};
for (const group of uniformGroups) {
- for (const [uniformName, uniformData] of Object.entries(group.uniforms)) {
+ for (const [uniformName, uniformData] of Object.entries(
+ group.uniforms
+ )) {
allUniforms[uniformName] = {
...uniformData,
group: group.group,
@@ -2304,11 +2458,13 @@ function rendererWebGPU(p5, fn) {
// Extract samplers from group bindings
const samplers = {};
// TODO: support other texture types
- const samplerRegex = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d|sampler);/g;
+ const samplerRegex =
+ /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d|sampler);/g;
// Extract storage buffers
const storageBuffers = {};
- const storageRegex = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*array<\w+>/g;
+ const storageRegex =
+ /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*array<\w+>/g;
// Track which bindings are taken by the struct properties we've parsed
// (the rest should be textures/samplers)
@@ -2340,16 +2496,16 @@ function rendererWebGPU(p5, fn) {
name,
type,
isSampler: true,
- noData: type === 'sampler',
+ noData: type === 'sampler'
};
}
for (const sampler of Object.values(samplers)) {
if (sampler.type.startsWith('texture')) {
const samplerName = sampler.name + '_sampler';
- const samplerNode = Object
- .values(samplers)
- .find((s) => s.name === samplerName);
+ const samplerNode = Object.values(samplers).find(
+ s => s.name === samplerName
+ );
if (!samplerNode) {
throw new Error(
`Every shader texture needs an accompanying sampler. Could not find sampler ${samplerName} for texture ${sampler.name}`
@@ -2368,9 +2524,10 @@ function rendererWebGPU(p5, fn) {
const key = `${groupIndex},${bindingIndex}`;
const existing = storageBuffers[key];
// If any stage uses read_write, the bind group layout must use read_write
- const finalAccessMode = (existing?.accessMode === 'read_write' || accessMode === 'read_write')
- ? 'read_write'
- : accessMode;
+ const finalAccessMode =
+ existing?.accessMode === 'read_write' || accessMode === 'read_write'
+ ? 'read_write'
+ : accessMode;
storageBuffers[key] = {
visibility: (existing?.visibility || 0) | visibility,
@@ -2387,7 +2544,11 @@ function rendererWebGPU(p5, fn) {
// Store storage buffers on shader for later use
shader._storageBuffers = Object.values(storageBuffers);
- return [...Object.values(allUniforms).sort((a, b) => a.index - b.index), ...Object.values(samplers), ...Object.values(storageBuffers)];
+ return [
+ ...Object.values(allUniforms).sort((a, b) => a.index - b.index),
+ ...Object.values(samplers),
+ ...Object.values(storageBuffers)
+ ];
}
getNextBindingIndex({ vert, frag, compute }, group = 0) {
@@ -2418,7 +2579,10 @@ function rendererWebGPU(p5, fn) {
uniform.texture =
data instanceof Texture ? data : this.getTexture(data);
} else if (!data?._isStorageBuffer) {
- uniform._mappedData = this._mapUniformData(uniform, uniform._cachedData);
+ uniform._mappedData = this._mapUniformData(
+ uniform,
+ uniform._cachedData
+ );
}
shader.buffersDirty.add(uniform.group * 1000 + uniform.binding);
}
@@ -2435,11 +2599,11 @@ function rendererWebGPU(p5, fn) {
const gpuTexture = this.device.createTexture({
size: [width, height],
format,
- usage: usage || (
+ usage:
+ usage ||
GPUTextureUsage.TEXTURE_BINDING |
- GPUTextureUsage.COPY_DST |
- GPUTextureUsage.RENDER_ATTACHMENT
- ),
+ GPUTextureUsage.COPY_DST |
+ GPUTextureUsage.RENDER_ATTACHMENT
});
return { gpuTexture, view: gpuTexture.createView() };
}
@@ -2482,12 +2646,12 @@ function rendererWebGPU(p5, fn) {
[constants.CLAMP]: 'clamp-to-edge',
[constants.REPEAT]: 'repeat',
[constants.MIRROR]: 'mirror-repeat'
- }
+ };
const sampler = this.device.createSampler({
magFilter: constantMapping[texture.magFilter],
minFilter: constantMapping[texture.minFilter],
addressModeU: constantMapping[texture.wrapS],
- addressModeV: constantMapping[texture.wrapT],
+ addressModeV: constantMapping[texture.wrapT]
});
this.samplers.set(key, sampler);
return sampler;
@@ -2507,16 +2671,16 @@ function rendererWebGPU(p5, fn) {
materialFragmentShader,
{
vertex: {
- "void beforeVertex": "() {}",
- "Vertex getObjectInputs": "(inputs: Vertex) { return inputs; }",
- "Vertex getWorldInputs": "(inputs: Vertex) { return inputs; }",
- "Vertex getCameraInputs": "(inputs: Vertex) { return inputs; }",
- "void afterVertex": "() {}",
+ 'void beforeVertex': '() {}',
+ 'Vertex getObjectInputs': '(inputs: Vertex) { return inputs; }',
+ 'Vertex getWorldInputs': '(inputs: Vertex) { return inputs; }',
+ 'Vertex getCameraInputs': '(inputs: Vertex) { return inputs; }',
+ 'void afterVertex': '() {}'
},
fragment: {
- "void beforeFragment": "() {}",
- "Inputs getPixelInputs": "(inputs: Inputs) { return inputs; }",
- "vec4f combineColors": `(components: ColorComponents) {
+ 'void beforeFragment': '() {}',
+ 'Inputs getPixelInputs': '(inputs: Inputs) { return inputs; }',
+ 'vec4f combineColors': `(components: ColorComponents) {
var rgb = vec3(0.0);
rgb += components.diffuse * components.baseColor;
rgb += components.ambient * components.ambientColor;
@@ -2524,9 +2688,10 @@ function rendererWebGPU(p5, fn) {
rgb += components.emissive;
return vec4(rgb, components.opacity);
}`,
- "vec4f getFinalColor": "(color: vec4, texCoord: vec2) { return color; }",
- "void afterFragment": "() {}",
- },
+ 'vec4f getFinalColor':
+ '(color: vec4, texCoord: vec2) { return color; }',
+ 'void afterFragment': '() {}'
+ }
}
);
}
@@ -2541,17 +2706,18 @@ function rendererWebGPU(p5, fn) {
colorFragmentShader,
{
vertex: {
- "void beforeVertex": "() {}",
- "Vertex getObjectInputs": "(inputs: Vertex) { return inputs; }",
- "Vertex getWorldInputs": "(inputs: Vertex) { return inputs; }",
- "Vertex getCameraInputs": "(inputs: Vertex) { return inputs; }",
- "void afterVertex": "() {}",
+ 'void beforeVertex': '() {}',
+ 'Vertex getObjectInputs': '(inputs: Vertex) { return inputs; }',
+ 'Vertex getWorldInputs': '(inputs: Vertex) { return inputs; }',
+ 'Vertex getCameraInputs': '(inputs: Vertex) { return inputs; }',
+ 'void afterVertex': '() {}'
},
fragment: {
- "void beforeFragment": "() {}",
- "vec4 getFinalColor": "(color: vec4, texCoord: vec2) { return color; }",
- "void afterFragment": "() {}",
- },
+ 'void beforeFragment': '() {}',
+ 'vec4 getFinalColor':
+ '(color: vec4, texCoord: vec2) { return color; }',
+ 'void afterFragment': '() {}'
+ }
}
);
}
@@ -2566,19 +2732,23 @@ function rendererWebGPU(p5, fn) {
lineDefs + lineFragmentShader,
{
vertex: {
- "void beforeVertex": "() {}",
- "StrokeVertex getObjectInputs": "(inputs: StrokeVertex) { return inputs; }",
- "StrokeVertex getWorldInputs": "(inputs: StrokeVertex) { return inputs; }",
- "StrokeVertex getCameraInputs": "(inputs: StrokeVertex) { return inputs; }",
- "void afterVertex": "() {}",
+ 'void beforeVertex': '() {}',
+ 'StrokeVertex getObjectInputs':
+ '(inputs: StrokeVertex) { return inputs; }',
+ 'StrokeVertex getWorldInputs':
+ '(inputs: StrokeVertex) { return inputs; }',
+ 'StrokeVertex getCameraInputs':
+ '(inputs: StrokeVertex) { return inputs; }',
+ 'void afterVertex': '() {}'
},
fragment: {
- "void beforeFragment": "() {}",
- "Inputs getPixelInputs": "(inputs: Inputs) { return inputs; }",
- "vec4 getFinalColor": "(color: vec4, texCoord: vec2) { return color; }",
- "bool shouldDiscard": "(outside: bool) { return outside; };",
- "void afterFragment": "() {}",
- },
+ 'void beforeFragment': '() {}',
+ 'Inputs getPixelInputs': '(inputs: Inputs) { return inputs; }',
+ 'vec4 getFinalColor':
+ '(color: vec4, texCoord: vec2) { return color; }',
+ 'bool shouldDiscard': '(outside: bool) { return outside; };',
+ 'void afterFragment': '() {}'
+ }
}
);
}
@@ -2620,9 +2790,9 @@ function rendererWebGPU(p5, fn) {
const activeFramebuffer = this.activeFramebuffer();
const depthTextureView = activeFramebuffer
- ? (activeFramebuffer.aaDepthTexture
- ? activeFramebuffer.aaDepthTextureView
- : activeFramebuffer.depthTextureView)
+ ? activeFramebuffer.aaDepthTexture
+ ? activeFramebuffer.aaDepthTextureView
+ : activeFramebuffer.depthTextureView
: this.depthTextureView;
if (!depthTextureView) {
@@ -2635,12 +2805,12 @@ function rendererWebGPU(p5, fn) {
stencilStoreOp: 'store',
stencilClearValue: 0,
depthReadOnly: true,
- stencilReadOnly: false,
+ stencilReadOnly: false
};
const renderPassDescriptor = {
colorAttachments: [],
- depthStencilAttachment: depthStencilAttachment,
+ depthStencilAttachment: depthStencilAttachment
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
@@ -2662,9 +2832,9 @@ function rendererWebGPU(p5, fn) {
const activeFramebuffer = this.activeFramebuffer();
const depthTextureView = activeFramebuffer
- ? (activeFramebuffer.aaDepthTexture
- ? activeFramebuffer.aaDepthTextureView
- : activeFramebuffer.depthTextureView)
+ ? activeFramebuffer.aaDepthTexture
+ ? activeFramebuffer.aaDepthTextureView
+ : activeFramebuffer.depthTextureView
: this.depthTextureView;
if (!depthTextureView) {
@@ -2677,12 +2847,12 @@ function rendererWebGPU(p5, fn) {
stencilStoreOp: 'store',
stencilClearValue: 1,
depthReadOnly: true,
- stencilReadOnly: false,
+ stencilReadOnly: false
};
const renderPassDescriptor = {
colorAttachments: [],
- depthStencilAttachment: depthStencilAttachment,
+ depthStencilAttachment: depthStencilAttachment
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
@@ -2722,10 +2892,9 @@ function rendererWebGPU(p5, fn) {
(_, condition, hookType, hookName, body) => {
const target = condition === 'ifdef';
if (
- (
- !!shader.hooks.modified.vertex[`${hookType} ${hookName}`] ||
- !!shader.hooks.modified.fragment[`${hookType} ${hookName}`]
- ) === target
+ (!!shader.hooks.modified.vertex[`${hookType} ${hookName}`] ||
+ !!shader.hooks.modified.fragment[`${hookType} ${hookName}`]) ===
+ target
) {
return body;
} else {
@@ -2734,14 +2903,23 @@ function rendererWebGPU(p5, fn) {
}
);
- let [preMain, main, postMain] = src.split(/((?:@(?:vertex|fragment|compute)\s*(?:@workgroup_size\([^)]+\)\s*)?)?fn main[^{]+\{)/);
+ let [preMain, main, postMain] = src.split(
+ /((?:@(?:vertex|fragment|compute)\s*(?:@workgroup_size\([^)]+\)\s*)?)?fn main[^{]+\{)/
+ );
const getBuiltinParamName = (mainSrc, builtinName) => {
- const match = new RegExp(`@builtin\\s*\\(\\s*${builtinName}\\s*\\)\\s*(\\w+)\\s*:`).exec(mainSrc);
+ const match = new RegExp(
+ `@builtin\\s*\\(\\s*${builtinName}\\s*\\)\\s*(\\w+)\\s*:`
+ ).exec(mainSrc);
return match ? match[1] : null;
};
- const ensureBuiltinParam = (mainSrc, builtinName, fallbackName, typeName) => {
+ const ensureBuiltinParam = (
+ mainSrc,
+ builtinName,
+ fallbackName,
+ typeName
+ ) => {
const existingName = getBuiltinParamName(mainSrc, builtinName);
if (existingName) {
return { mainSrc, argName: existingName };
@@ -2756,16 +2934,22 @@ function rendererWebGPU(p5, fn) {
return { mainSrc: injectedMain, argName: fallbackName };
};
- const getMainStructParameter = (mainSrc) => {
+ const getMainStructParameter = mainSrc => {
const match = /fn main\s*\(\s*(\w+)\s*:\s*(\w+)/.exec(mainSrc);
if (!match) return null;
return { inputName: match[1], structName: match[2] };
};
const getStructBuiltinFieldName = (structName, builtinName) => {
- const structMatch = new RegExp(`struct\\s+${structName}\\s*\\{([^}]*)\\}`, 's').exec(preMain);
+ const structMatch = new RegExp(
+ `struct\\s+${structName}\\s*\\{([^}]*)\\}`,
+ 's'
+ ).exec(preMain);
if (!structMatch) return null;
- const fieldMatch = new RegExp(`@builtin\\s*\\(\\s*${builtinName}\\s*\\)\\s*(\\w+)\\s*:`, 's').exec(structMatch[1]);
+ const fieldMatch = new RegExp(
+ `@builtin\\s*\\(\\s*${builtinName}\\s*\\)\\s*(\\w+)\\s*:`,
+ 's'
+ ).exec(structMatch[1]);
return fieldMatch ? fieldMatch[1] : null;
};
@@ -2779,10 +2963,20 @@ function rendererWebGPU(p5, fn) {
let hookExtraArgs = [];
if (shaderType === 'vertex') {
- const ensuredInstance = ensureBuiltinParam(main, 'instance_index', 'instanceID', 'u32');
+ const ensuredInstance = ensureBuiltinParam(
+ main,
+ 'instance_index',
+ 'instanceID',
+ 'u32'
+ );
main = ensuredInstance.mainSrc;
- const ensuredVertex = ensureBuiltinParam(main, 'vertex_index', '_p5VertexId', 'u32');
+ const ensuredVertex = ensureBuiltinParam(
+ main,
+ 'vertex_index',
+ '_p5VertexId',
+ 'u32'
+ );
main = ensuredVertex.mainSrc;
hookExtraParams = ['instanceID: u32', '_p5VertexId: u32'];
@@ -2794,7 +2988,10 @@ function rendererWebGPU(p5, fn) {
if (!fragmentPositionArg) {
const mainStructParam = getMainStructParameter(main);
if (mainStructParam) {
- const positionField = getStructBuiltinFieldName(mainStructParam.structName, 'position');
+ const positionField = getStructBuiltinFieldName(
+ mainStructParam.structName,
+ 'position'
+ );
if (positionField) {
fragmentPositionArg = `${mainStructParam.inputName}.${positionField}`;
}
@@ -2802,7 +2999,12 @@ function rendererWebGPU(p5, fn) {
}
if (!fragmentPositionArg) {
- const ensuredPosition = ensureBuiltinParam(main, 'position', '_p5FragPos', 'vec4');
+ const ensuredPosition = ensureBuiltinParam(
+ main,
+ 'position',
+ '_p5FragPos',
+ 'vec4'
+ );
main = ensuredPosition.mainSrc;
fragmentPositionArg = ensuredPosition.argName;
}
@@ -2810,7 +3012,12 @@ function rendererWebGPU(p5, fn) {
hookExtraParams = ['_p5FragPos: vec4'];
hookExtraArgs = [fragmentPositionArg];
} else if (shaderType === 'compute') {
- const ensuredGlobalId = ensureBuiltinParam(main, 'global_invocation_id', '_p5GlobalId', 'vec3');
+ const ensuredGlobalId = ensureBuiltinParam(
+ main,
+ 'global_invocation_id',
+ '_p5GlobalId',
+ 'vec3'
+ );
main = ensuredGlobalId.mainSrc;
hookExtraParams = ['_p5GlobalId: vec3'];
@@ -2831,11 +3038,29 @@ function rendererWebGPU(p5, fn) {
// Find the next available binding in group 0
// Use the source we're currently building (preMain) which has texture bindings. We can't call `fragSrc()`
// or `vertSrc()` because we may be in one of those calls already, and might infinite loop
- const nextBinding = this.getNextBindingIndex({
- vert: shaderType === 'vertex' ? preMain + (shader.hooks.vertex?.declarations ?? '') + shader.hooks.declarations : shader._vertSrc,
- frag: shaderType === 'fragment' ? preMain + (shader.hooks.fragment?.declarations ?? '') + shader.hooks.declarations : shader._fragSrc,
- compute: shaderType === 'compute' ? preMain + (shader.hooks.compute?.declarations ?? '') + shader.hooks.declarations : shader._computeSrc,
- }, 0);
+ const nextBinding = this.getNextBindingIndex(
+ {
+ vert:
+ shaderType === 'vertex'
+ ? preMain +
+ (shader.hooks.vertex?.declarations ?? '') +
+ shader.hooks.declarations
+ : shader._vertSrc,
+ frag:
+ shaderType === 'fragment'
+ ? preMain +
+ (shader.hooks.fragment?.declarations ?? '') +
+ shader.hooks.declarations
+ : shader._fragSrc,
+ compute:
+ shaderType === 'compute'
+ ? preMain +
+ (shader.hooks.compute?.declarations ?? '') +
+ shader.hooks.declarations
+ : shader._computeSrc
+ },
+ 0
+ );
// Create HookUniforms struct and binding
const hookUniformsDecl = `
@@ -2846,7 +3071,10 @@ ${hookUniformFields}}
@group(0) @binding(${nextBinding}) var hooks: HookUniforms;
`;
// Insert before the first @group binding, or at the end if there are none
- const replaced = preMain.replace(/(@group\(0\)\s+@binding)/, `${hookUniformsDecl}\n$1`);
+ const replaced = preMain.replace(
+ /(@group\(0\)\s+@binding)/,
+ `${hookUniformsDecl}\n$1`
+ );
if (replaced === preMain) {
// No @group bindings found in base shader, append to preMain
preMain = preMain + '\n' + hookUniformsDecl;
@@ -2856,9 +3084,15 @@ ${hookUniformFields}}
}
// Handle varying variables by injecting them into VertexOutput and FragmentInput structs
- if (shader.hooks.varyingVariables && shader.hooks.varyingVariables.length > 0) {
+ if (
+ shader.hooks.varyingVariables &&
+ shader.hooks.varyingVariables.length > 0
+ ) {
// Generate struct members for varying variables
- let nextLocationIndex = this._getNextAvailableLocation(preMain, shaderType);
+ let nextLocationIndex = this._getNextAvailableLocation(
+ preMain,
+ shaderType
+ );
let varyingMembers = '';
for (const varyingVar of shader.hooks.varyingVariables) {
@@ -2883,7 +3117,10 @@ ${hookUniformFields}}
}
// Add file-global varying variable declarations
- if (shader.hooks.varyingVariables && shader.hooks.varyingVariables.length > 0) {
+ if (
+ shader.hooks.varyingVariables &&
+ shader.hooks.varyingVariables.length > 0
+ ) {
let varyingDeclarations = '';
for (const varyingVar of shader.hooks.varyingVariables) {
// varyingVar is a string like "varName: vec3"
@@ -2906,9 +3143,15 @@ ${hookUniformFields}}
const returnMatch = postMain.match(/return\s+(\w+)\s*;/);
if (returnMatch) {
const outputVarName = returnMatch[1];
- copyStatements = copyStatements.replace(/OUTPUT_VAR/g, outputVarName);
+ copyStatements = copyStatements.replace(
+ /OUTPUT_VAR/g,
+ outputVarName
+ );
// Insert before the return statement
- postMain = postMain.replace(/(return\s+\w+\s*;)/g, `${copyStatements} $1`);
+ postMain = postMain.replace(
+ /(return\s+\w+\s*;)/g,
+ `${copyStatements} $1`
+ );
}
} else if (shaderType === 'fragment') {
// In fragment shader, initialize varying variables from input struct at start of main
@@ -2930,9 +3173,15 @@ ${hookUniformFields}}
// Handle instanceID varying for fragment access
if (shader.hooks.instanceIDVarying) {
- const { name, declaration, source, interpolation } = shader.hooks.instanceIDVarying;
- const nextLocIndex = this._getNextAvailableLocation(preMain, shaderType);
- const interpAttr = interpolation ? ` @interpolate(${interpolation})` : '';
+ const { name, declaration, source, interpolation } =
+ shader.hooks.instanceIDVarying;
+ const nextLocIndex = this._getNextAvailableLocation(
+ preMain,
+ shaderType
+ );
+ const interpAttr = interpolation
+ ? ` @interpolate(${interpolation})`
+ : '';
const [varName, varType] = declaration.split(':').map(s => s.trim());
const structMember = `@location(${nextLocIndex})${interpAttr} ${declaration},`;
@@ -2967,7 +3216,8 @@ ${hookUniformFields}}
const mainStructParam = getMainStructParameter(main);
if (mainStructParam) {
const inputVarName = mainStructParam.inputName;
- postMain = `\n ${varName} = ${inputVarName}.${varName};\n` + postMain;
+ postMain =
+ `\n ${varName} = ${inputVarName}.${varName};\n` + postMain;
}
}
}
@@ -2982,7 +3232,9 @@ ${hookUniformFields}}
}
for (const hookDef in shader.hooks.helpers) {
const [hookType, hookName] = hookDef.split(' ');
- const [_, params, body] = /^(\([^\)]*\))((?:.|\n)*)$/.exec(shader.hooks.helpers[hookDef]);
+ const [_, params, body] = /^(\([^)]*\))((?:.|\n)*)$/.exec(
+ shader.hooks.helpers[hookDef]
+ );
if (hookType === 'void') {
hooks += `fn ${hookName}${params}${body}\n`;
} else {
@@ -2999,7 +3251,9 @@ ${hookUniformFields}}
shader.hooks.modified[shaderType][hookDef] ? 'true' : 'false'
};\n`;
- let [_, params, body] = /^(\([^\)]*\))((?:.|\n)*)$/.exec(shader.hooks[shaderType][hookDef]);
+ let [_, params, body] = /^(\([^)]*\))((?:.|\n)*)$/.exec(
+ shader.hooks[shaderType][hookDef]
+ );
params = appendHookParams(params, hookExtraParams);
@@ -3015,7 +3269,7 @@ ${hookUniformFields}}
// extra args from right to left so position shifts don't
// invalidate earlier insertion points.
if (hookExtraArgs.length > 0) {
- const addHookArgs = (src) => {
+ const addHookArgs = src => {
const insertions = [];
let searchIdx = 0;
let m;
@@ -3042,7 +3296,8 @@ ${hookUniformFields}}
let result = src;
for (const { pos, hasParams } of insertions) {
- const insertion = (hasParams ? ', ' : '') + hookExtraArgs.join(', ');
+ const insertion =
+ (hasParams ? ', ' : '') + hookExtraArgs.join(', ');
result = result.slice(0, pos) + insertion + result.slice(pos);
}
return result;
@@ -3057,10 +3312,13 @@ ${hookUniformFields}}
_getNextAvailableLocation(shaderSource, shaderType) {
// Parse existing struct to find the highest @location number
let maxLocation = -1;
- const structName = shaderType === 'vertex' ? 'VertexOutput' : 'FragmentInput';
+ const structName =
+ shaderType === 'vertex' ? 'VertexOutput' : 'FragmentInput';
// Find the struct definition
- const structMatch = shaderSource.match(new RegExp(`struct\\s+${structName}\\s*\\{([^}]*)\\}`, 's'));
+ const structMatch = shaderSource.match(
+ new RegExp(`struct\\s+${structName}\\s*\\{([^}]*)\\}`, 's')
+ );
if (structMatch) {
const structBody = structMatch[1];
@@ -3080,18 +3338,18 @@ ${hookUniformFields}}
getShaderHookTypes(shader, hookName) {
// Create mapping from WGSL types to DataType entries
const wgslToDataType = {
- 'f32': DataType.float1,
+ f32: DataType.float1,
'vec2': DataType.float2,
'vec3': DataType.float3,
'vec4': DataType.float4,
- 'vec2f': DataType.float2,
- 'vec3f': DataType.float3,
- 'vec4f': DataType.float4,
- 'i32': DataType.int1,
+ vec2f: DataType.float2,
+ vec3f: DataType.float3,
+ vec4f: DataType.float4,
+ i32: DataType.int1,
'vec2': DataType.int2,
'vec3': DataType.int3,
'vec4': DataType.int4,
- 'bool': DataType.bool1,
+ bool: DataType.bool1,
'vec2': DataType.bool2,
'vec3': DataType.bool3,
'vec4': DataType.bool4,
@@ -3118,14 +3376,18 @@ ${hookUniformFields}}
const functionName = nameParts.pop();
const returnType = nameParts.pop();
const returnQualifiers = [...nameParts];
- const parameterMatch = /\(([^\)]*)\)/.exec(body);
+ const parameterMatch = /\(([^)]*)\)/.exec(body);
if (!parameterMatch) {
- throw new Error(`Couldn't find function parameters in hook body:\n${body}`);
+ throw new Error(
+ `Couldn't find function parameters in hook body:\n${body}`
+ );
}
const structProperties = structName => {
// WGSL struct parsing: struct StructName { field1: Type, field2: Type }
- const structDefMatch = new RegExp(`struct\\s+${structName}\\s*{([^}]*)}`).exec(fullSrc);
+ const structDefMatch = new RegExp(
+ `struct\\s+${structName}\\s*{([^}]*)}`
+ ).exec(fullSrc);
if (!structDefMatch) return undefined;
const properties = [];
@@ -3136,7 +3398,8 @@ ${hookUniformFields}}
// Remove location decorations and parse field
// Format: [@location(N)] fieldName: Type
- const fieldMatch = /(?:@location\([^)]*\)\s*)?(\w+)\s*:\s*([^,\s]+)/.exec(trimmed);
+ const fieldMatch =
+ /(?:@location\([^)]*\)\s*)?(\w+)\s*:\s*([^,\s]+)/.exec(trimmed);
if (!fieldMatch) continue;
const name = fieldMatch[1];
@@ -3158,35 +3421,38 @@ ${hookUniformFields}}
return properties;
};
- const parameters = parameterMatch[1].split(',').map(paramString => {
- // WGSL function parameters: name: type or name: binding
- const trimmed = paramString.trim();
- if (!trimmed) return null;
+ const parameters = parameterMatch[1]
+ .split(',')
+ .map(paramString => {
+ // WGSL function parameters: name: type or name: binding
+ const trimmed = paramString.trim();
+ if (!trimmed) return null;
- const parts = trimmed.split(':').map(s => s.trim());
- if (parts.length !== 2) return null;
+ const parts = trimmed.split(':').map(s => s.trim());
+ if (parts.length !== 2) return null;
- const name = parts[0];
- let typeName = parts[1];
+ const name = parts[0];
+ let typeName = parts[1];
- // Handle texture bindings like "texture_2d" -> sampler2D DataType
- if (typeName.includes('texture_2d')) {
- typeName = 'texture_2d';
- }
+ // Handle texture bindings like "texture_2d" -> sampler2D DataType
+ if (typeName.includes('texture_2d')) {
+ typeName = 'texture_2d';
+ }
- const dataType = wgslToDataType[typeName] || null;
+ const dataType = wgslToDataType[typeName] || null;
- const properties = structProperties(typeName);
- return {
- name,
- type: {
- typeName: typeName, // Keep native WGSL type name
- qualifiers: [],
- properties,
- dataType: dataType
- }
- };
- }).filter(Boolean);
+ const properties = structProperties(typeName);
+ return {
+ name,
+ type: {
+ typeName: typeName, // Keep native WGSL type name
+ qualifiers: [],
+ properties,
+ dataType: dataType
+ }
+ };
+ })
+ .filter(Boolean);
// Convert WGSL return type to DataType
const returnDataType = wgslToDataType[returnType] || null;
@@ -3221,7 +3487,7 @@ ${hookUniformFields}}
const bufferSize = Math.max(requiredSize, this.pixelReadBufferSize * 2);
this.pixelReadBuffer = this.device.createBuffer({
size: bufferSize,
- usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
});
this.pixelReadBufferSize = bufferSize;
}
@@ -3238,7 +3504,7 @@ ${hookUniformFields}}
//////////////////////////////////////////////
defaultFramebufferAlpha() {
- return true
+ return true;
}
defaultFramebufferAntialias() {
@@ -3249,15 +3515,16 @@ ${hookUniformFields}}
return true;
}
- createFramebufferResources(framebuffer) {
- }
+ createFramebufferResources(framebuffer) {}
validateFramebufferFormats(framebuffer) {
- if (![
- constants.UNSIGNED_BYTE,
- constants.FLOAT,
- constants.HALF_FLOAT
- ].includes(framebuffer.format)) {
+ if (
+ ![
+ constants.UNSIGNED_BYTE,
+ constants.FLOAT,
+ constants.HALF_FLOAT
+ ].includes(framebuffer.format)
+ ) {
console.warn(
'Unknown Framebuffer format. ' +
'Please use UNSIGNED_BYTE, FLOAT, or HALF_FLOAT. ' +
@@ -3266,10 +3533,12 @@ ${hookUniformFields}}
framebuffer.format = constants.UNSIGNED_BYTE;
}
- if (framebuffer.useDepth && ![
- constants.UNSIGNED_INT,
- constants.FLOAT
- ].includes(framebuffer.depthFormat)) {
+ if (
+ framebuffer.useDepth &&
+ ![constants.UNSIGNED_INT, constants.FLOAT].includes(
+ framebuffer.depthFormat
+ )
+ ) {
console.warn(
'Unknown Framebuffer depth format. ' +
'Please use UNSIGNED_INT or FLOAT. Defaulting to FLOAT.'
@@ -3306,21 +3575,24 @@ ${hookUniformFields}}
size: {
width: framebuffer.width * framebuffer.density,
height: framebuffer.height * framebuffer.density,
- depthOrArrayLayers: 1,
+ depthOrArrayLayers: 1
},
- format: this._getWebGPUColorFormat(framebuffer),
+ format: this._getWebGPUColorFormat(framebuffer)
};
// Create non-multisampled texture for texture binding (always needed)
const colorTextureDescriptor = {
...baseDescriptor,
- usage: GPUTextureUsage.RENDER_ATTACHMENT |
- GPUTextureUsage.TEXTURE_BINDING |
- GPUTextureUsage.COPY_SRC |
- (framebuffer._useCanvasFormat ? GPUTextureUsage.COPY_DST : 0),
- sampleCount: 1,
+ usage:
+ GPUTextureUsage.RENDER_ATTACHMENT |
+ GPUTextureUsage.TEXTURE_BINDING |
+ GPUTextureUsage.COPY_SRC |
+ (framebuffer._useCanvasFormat ? GPUTextureUsage.COPY_DST : 0),
+ sampleCount: 1
};
- framebuffer.colorTexture = this.device.createTexture(colorTextureDescriptor);
+ framebuffer.colorTexture = this.device.createTexture(
+ colorTextureDescriptor
+ );
framebuffer.colorTextureView = framebuffer.colorTexture.createView();
// Create multisampled texture for rendering if antialiasing is enabled
@@ -3328,10 +3600,13 @@ ${hookUniformFields}}
const aaColorTextureDescriptor = {
...baseDescriptor,
usage: GPUTextureUsage.RENDER_ATTACHMENT,
- sampleCount: this._getValidSampleCount(framebuffer.antialiasSamples),
+ sampleCount: this._getValidSampleCount(framebuffer.antialiasSamples)
};
- framebuffer.aaColorTexture = this.device.createTexture(aaColorTextureDescriptor);
- framebuffer.aaColorTextureView = framebuffer.aaColorTexture.createView();
+ framebuffer.aaColorTexture = this.device.createTexture(
+ aaColorTextureDescriptor
+ );
+ framebuffer.aaColorTextureView =
+ framebuffer.aaColorTexture.createView();
}
if (framebuffer.useDepth) {
@@ -3339,20 +3614,23 @@ ${hookUniformFields}}
size: {
width: framebuffer.width * framebuffer.density,
height: framebuffer.height * framebuffer.density,
- depthOrArrayLayers: 1,
+ depthOrArrayLayers: 1
},
- format: this._getWebGPUDepthFormat(framebuffer),
+ format: this._getWebGPUDepthFormat(framebuffer)
};
// Create non-multisampled depth texture for texture binding (always needed)
const depthTextureDescriptor = {
...depthBaseDescriptor,
- usage: GPUTextureUsage.RENDER_ATTACHMENT |
- GPUTextureUsage.TEXTURE_BINDING |
- (framebuffer._useCanvasFormat ? GPUTextureUsage.COPY_DST : 0),
- sampleCount: 1,
+ usage:
+ GPUTextureUsage.RENDER_ATTACHMENT |
+ GPUTextureUsage.TEXTURE_BINDING |
+ (framebuffer._useCanvasFormat ? GPUTextureUsage.COPY_DST : 0),
+ sampleCount: 1
};
- framebuffer.depthTexture = this.device.createTexture(depthTextureDescriptor);
+ framebuffer.depthTexture = this.device.createTexture(
+ depthTextureDescriptor
+ );
framebuffer.depthTextureView = framebuffer.depthTexture.createView();
// Create multisampled depth texture for rendering if antialiasing is enabled
@@ -3360,10 +3638,13 @@ ${hookUniformFields}}
const aaDepthTextureDescriptor = {
...depthBaseDescriptor,
usage: GPUTextureUsage.RENDER_ATTACHMENT,
- sampleCount: this._getValidSampleCount(framebuffer.antialiasSamples),
+ sampleCount: this._getValidSampleCount(framebuffer.antialiasSamples)
};
- framebuffer.aaDepthTexture = this.device.createTexture(aaDepthTextureDescriptor);
- framebuffer.aaDepthTextureView = framebuffer.aaDepthTexture.createView();
+ framebuffer.aaDepthTexture = this.device.createTexture(
+ aaDepthTextureDescriptor
+ );
+ framebuffer.aaDepthTextureView =
+ framebuffer.aaDepthTexture.createView();
}
}
@@ -3380,32 +3661,35 @@ ${hookUniformFields}}
view: framebuffer.aaColorTexture
? framebuffer.aaColorTextureView
: framebuffer.colorTextureView,
- loadOp: "clear",
- storeOp: "store",
+ loadOp: 'clear',
+ storeOp: 'store',
clearValue: { r: 0, g: 0, b: 0, a: 0 },
resolveTarget: framebuffer.aaColorTexture
? framebuffer.colorTextureView
- : undefined,
+ : undefined
};
// Clear the depth texture if it exists
- const depthTexture = framebuffer.aaDepthTexture || framebuffer.depthTexture;
- const depthStencilAttachment = depthTexture ? {
- view: framebuffer.aaDepthTexture
- ? framebuffer.aaDepthTextureView
- : framebuffer.depthTextureView,
- depthLoadOp: "clear",
- depthStoreOp: "store",
- depthClearValue: 1.0,
- stencilLoadOp: "clear",
- stencilStoreOp: "store",
- depthReadOnly: false,
- stencilReadOnly: false,
- } : undefined;
+ const depthTexture =
+ framebuffer.aaDepthTexture || framebuffer.depthTexture;
+ const depthStencilAttachment = depthTexture
+ ? {
+ view: framebuffer.aaDepthTexture
+ ? framebuffer.aaDepthTextureView
+ : framebuffer.depthTextureView,
+ depthLoadOp: 'clear',
+ depthStoreOp: 'store',
+ depthClearValue: 1.0,
+ stencilLoadOp: 'clear',
+ stencilStoreOp: 'store',
+ depthReadOnly: false,
+ stencilReadOnly: false
+ }
+ : undefined;
const renderPassDescriptor = {
colorAttachments: [colorAttachment],
- depthStencilAttachment: depthStencilAttachment,
+ depthStencilAttachment: depthStencilAttachment
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
@@ -3444,7 +3728,9 @@ ${hookUniformFields}}
if (value.dimensions === 2) return 'vec2f';
if (value.dimensions === 3) return 'vec3f';
if (value.dimensions === 4) return 'vec4f';
- throw new Error(`Unsupported vector dimension ${value.dimensions} for struct storage field`);
+ throw new Error(
+ `Unsupported vector dimension ${value.dimensions} for struct storage field`
+ );
}
if (value?.isColor) {
return 'vec4f';
@@ -3453,9 +3739,13 @@ ${hookUniformFields}}
if (value.length === 2) return 'vec2f';
if (value.length === 3) return 'vec3f';
if (value.length === 4) return 'vec4f';
- throw new Error(`Unsupported array length ${value.length} for struct storage field`);
+ throw new Error(
+ `Unsupported array length ${value.length} for struct storage field`
+ );
}
- throw new Error(`Unsupported value type ${typeof value} for struct storage field`);
+ throw new Error(
+ `Unsupported value type ${typeof value} for struct storage field`
+ );
}
// Infers a struct schema from the first element of a struct array.
@@ -3482,16 +3772,16 @@ ${hookUniformFields}}
) {
p5._friendlyError(
`The "${name}" property in your storage data contains a nested object. ` +
- `Make sure you only use properties with numbers, arrays of numbers, or p5.Vector.`,
+ `Make sure you only use properties with numbers, arrays of numbers, or p5.Vector.`,
'createStorage'
);
}
}
}
- const fieldLines = entries.map(([name, value]) =>
- ` ${name}: ${this._jsValueToWgslType(value)},`
- ).join('\n');
+ const fieldLines = entries
+ .map(([name, value]) => ` ${name}: ${this._jsValueToWgslType(value)},`)
+ .join('\n');
const structBody = `{\n${fieldLines}\n}`;
const elements = this._parseStruct(`struct _Tmp ${structBody}`, '_Tmp');
@@ -3504,9 +3794,11 @@ ${hookUniformFields}}
const align = el.size <= 4 ? 4 : el.size <= 8 ? 8 : 16;
maxAlign = Math.max(maxAlign, align);
// Track original JS type for reconstruction during readback
- const kind = value?.isVector ? 'vector'
- : value?.isColor ? 'color'
- : undefined;
+ const kind = value?.isVector
+ ? 'vector'
+ : value?.isColor
+ ? 'color'
+ : undefined;
return {
name,
baseType: el.baseType,
@@ -3514,7 +3806,7 @@ ${hookUniformFields}}
offset: el.offset,
packInPlace: el.packInPlace ?? false,
dim: el.size / 4,
- kind,
+ kind
};
});
@@ -3535,7 +3827,13 @@ ${hookUniformFields}}
const item = data[i];
const baseOffset = i * stride;
for (const field of fields) {
- this._packField(field, item[field.name], floatView, dataView, baseOffset);
+ this._packField(
+ field,
+ item[field.name],
+ floatView,
+ dataView,
+ baseOffset
+ );
}
}
return floatView;
@@ -3585,8 +3883,10 @@ ${hookUniformFields}}
// Scale back to the current colorMode range
const maxes = this.states.colorMaxes[this.states.colorMode];
item[field.name] = this._pInst.color(
- values[0] * maxes[0], values[1] * maxes[1],
- values[2] * maxes[2], values[3] * maxes[3]
+ values[0] * maxes[0],
+ values[1] * maxes[1],
+ values[2] * maxes[2],
+ values[3] * maxes[3]
);
} else {
item[field.name] = values;
@@ -3604,20 +3904,25 @@ ${hookUniformFields}}
const device = this.device;
// Struct array: an array of plain objects
- if (Array.isArray(dataOrCount) && dataOrCount.length > 0 &&
- typeof dataOrCount[0] === 'object' && !Array.isArray(dataOrCount[0])) {
+ if (
+ Array.isArray(dataOrCount) &&
+ dataOrCount.length > 0 &&
+ typeof dataOrCount[0] === 'object' &&
+ !Array.isArray(dataOrCount[0])
+ ) {
if (!p5.disableFriendlyErrors && dataOrCount.length > 1) {
const firstKeys = Object.keys(dataOrCount[0]);
let warned = false;
for (let i = 1; i < dataOrCount.length; i++) {
const el = dataOrCount[i];
const elKeys = Object.keys(el);
- const sameKeys = firstKeys.length === elKeys.length &&
+ const sameKeys =
+ firstKeys.length === elKeys.length &&
firstKeys.every((k, j) => k === elKeys[j]);
if (!sameKeys) {
p5._friendlyError(
`Element ${i} has different fields than element 0. ` +
- `All elements should have the same properties.`,
+ `All elements should have the same properties.`,
'createStorage'
);
break;
@@ -3628,7 +3933,7 @@ ${hookUniformFields}}
if (firstType !== elType) {
p5._friendlyError(
`The "${key}" property of element ${i} has type ${elType} ` +
- `but element 0 has type ${firstType}. Proporties should have the same type across all elements.`,
+ `but element 0 has type ${firstType}. Proporties should have the same type across all elements.`,
'createStorage'
);
warned = true;
@@ -3643,8 +3948,11 @@ ${hookUniformFields}}
const size = packed.byteLength;
const buffer = device.createBuffer({
size,
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
- mappedAtCreation: true,
+ usage:
+ GPUBufferUsage.STORAGE |
+ GPUBufferUsage.COPY_DST |
+ GPUBufferUsage.COPY_SRC,
+ mappedAtCreation: true
});
new Float32Array(buffer.getMappedRange()).set(packed);
buffer.unmap();
@@ -3666,7 +3974,9 @@ ${hookUniformFields}}
} else if (Array.isArray(dataOrCount)) {
initialData = new Float32Array(dataOrCount);
} else {
- throw new Error('createStorage expects a number or array/Float32Array');
+ throw new Error(
+ 'createStorage expects a number or array/Float32Array'
+ );
}
size = initialData.byteLength;
}
@@ -3677,7 +3987,10 @@ ${hookUniformFields}}
// Create storage buffer with STORAGE | COPY_DST | COPY_SRC usage
const buffer = device.createBuffer({
size,
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
+ usage:
+ GPUBufferUsage.STORAGE |
+ GPUBufferUsage.COPY_DST |
+ GPUBufferUsage.COPY_SRC,
mappedAtCreation: initialData.length > 0
});
@@ -3716,9 +4029,13 @@ ${hookUniformFields}}
return this.depthFormat;
}
if (framebuffer.useStencil) {
- return framebuffer.depthFormat === constants.FLOAT ? 'depth32float-stencil8' : 'depth24plus-stencil8';
+ return framebuffer.depthFormat === constants.FLOAT
+ ? 'depth32float-stencil8'
+ : 'depth24plus-stencil8';
} else {
- return framebuffer.depthFormat === constants.FLOAT ? 'depth32float' : 'depth24plus';
+ return framebuffer.depthFormat === constants.FLOAT
+ ? 'depth32float'
+ : 'depth24plus';
}
}
@@ -3733,7 +4050,7 @@ ${hookUniformFields}}
}
deleteFramebufferTextures(framebuffer) {
- this._deleteFramebufferTexture(framebuffer.color)
+ this._deleteFramebufferTexture(framebuffer.color);
if (framebuffer.depth) this._deleteFramebufferTexture(framebuffer.depth);
}
@@ -3753,8 +4070,7 @@ ${hookUniformFields}}
}
}
- getFramebufferToBind(framebuffer) {
- }
+ getFramebufferToBind(framebuffer) {}
updateFramebufferTexture(framebuffer, property) {
// No-op for WebGPU since antialiasing is handled at pipeline level
@@ -3782,7 +4098,7 @@ ${hookUniformFields}}
// const stagingBuffer = this._ensurePixelReadBuffer(bufferSize);
const stagingBuffer = this.device.createBuffer({
size: bufferSize,
- usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
});
const commandEncoder = this.device.createCommandEncoder();
@@ -3793,7 +4109,11 @@ ${hookUniformFields}}
mipLevel: 0,
aspect: 'all'
},
- { buffer: stagingBuffer, bytesPerRow: alignedBytesPerRow, rowsPerImage: height },
+ {
+ buffer: stagingBuffer,
+ bytesPerRow: alignedBytesPerRow,
+ rowsPerImage: height
+ },
{ width, height, depthOrArrayLayers: 1 }
);
@@ -3808,7 +4128,9 @@ ${hookUniformFields}}
// If alignment was needed, extract the actual pixel data
let result;
if (alignedBytesPerRow === unalignedBytesPerRow) {
- result = new Uint8Array(mappedRange.slice(0, width * height * bytesPerPixel));
+ result = new Uint8Array(
+ mappedRange.slice(0, width * height * bytesPerPixel)
+ );
stagingBuffer.unmap();
} else {
// Need to extract pixel data from aligned buffer
@@ -3817,7 +4139,10 @@ ${hookUniformFields}}
for (let y = 0; y < height; y++) {
const srcOffset = y * alignedBytesPerRow;
const dstOffset = y * unalignedBytesPerRow;
- result.set(mappedData.subarray(srcOffset, srcOffset + unalignedBytesPerRow), dstOffset);
+ result.set(
+ mappedData.subarray(srcOffset, srcOffset + unalignedBytesPerRow),
+ dstOffset
+ );
}
stagingBuffer.unmap();
}
@@ -3856,7 +4181,6 @@ ${hookUniformFields}}
const pixelData = new Uint8Array(mappedRange);
const result = [pixelData[0], pixelData[1], pixelData[2], pixelData[3]];
-
this._ensurePixelsAreRGBA(framebuffer, result);
stagingBuffer.unmap();
@@ -3868,8 +4192,8 @@ ${hookUniformFields}}
// await this.finishDraw();
// const wasActive = this.activeFramebuffer() === framebuffer;
// if (wasActive) {
- // framebuffer.end();
- // this.flushDraw()
+ // framebuffer.end();
+ // this.flushDraw()
// }
// Ensure all pending GPU work is complete before reading pixels
// await this.queue.onSubmittedWorkDone();
@@ -3888,7 +4212,11 @@ ${hookUniformFields}}
{
texture: framebuffer.colorTexture,
mipLevel: 0,
- origin: { x: x * framebuffer.density, y: y * framebuffer.density, z: 0 }
+ origin: {
+ x: x * framebuffer.density,
+ y: y * framebuffer.density,
+ z: 0
+ }
},
{ buffer: stagingBuffer, bytesPerRow: alignedBytesPerRow },
{ width, height, depthOrArrayLayers: 1 }
@@ -3901,7 +4229,9 @@ ${hookUniformFields}}
let pixelData;
if (alignedBytesPerRow === unalignedBytesPerRow) {
- pixelData = new Uint8Array(mappedRange.slice(0, width * height * bytesPerPixel));
+ pixelData = new Uint8Array(
+ mappedRange.slice(0, width * height * bytesPerPixel)
+ );
} else {
// Need to extract pixel data from aligned buffer
pixelData = new Uint8Array(width * height * bytesPerPixel);
@@ -3909,14 +4239,19 @@ ${hookUniformFields}}
for (let y = 0; y < height; y++) {
const srcOffset = y * alignedBytesPerRow;
const dstOffset = y * unalignedBytesPerRow;
- pixelData.set(mappedData.subarray(srcOffset, srcOffset + unalignedBytesPerRow), dstOffset);
+ pixelData.set(
+ mappedData.subarray(srcOffset, srcOffset + unalignedBytesPerRow),
+ dstOffset
+ );
}
}
this._ensurePixelsAreRGBA(framebuffer, pixelData);
// WebGPU doesn't need vertical flipping unlike WebGL
const region = new Image(width, height);
- region.imageData = region.canvas.getContext('2d').createImageData(width, height);
+ region.imageData = region.canvas
+ .getContext('2d')
+ .createImageData(width, height);
region.imageData.data.set(pixelData);
region.pixels = region.imageData.data;
region.updatePixels();
@@ -3959,7 +4294,10 @@ ${hookUniformFields}}
_ensurePixelsAreRGBA(framebuffer, result) {
// Convert BGRA to RGBA if reading from canvas-format framebuffer on BGRA systems
- if (framebuffer._useCanvasFormat && this.presentationFormat === 'bgra8unorm') {
+ if (
+ framebuffer._useCanvasFormat &&
+ this.presentationFormat === 'bgra8unorm'
+ ) {
this._convertBGRtoRGB(result);
}
}
@@ -3992,7 +4330,6 @@ ${hookUniformFields}}
return super.filter(...args);
}
-
baseFilterShader() {
if (!this._baseFilterShader) {
this._baseFilterShader = new Shader(
@@ -4002,13 +4339,13 @@ ${hookUniformFields}}
{
vertex: {},
fragment: {
- "vec4 getColor": `(inputs: FilterInputs, canvasContent: texture_2d, canvasContent_sampler: sampler) -> vec4 {
+ 'vec4 getColor': `(inputs: FilterInputs, canvasContent: texture_2d, canvasContent_sampler: sampler) -> vec4 {
return textureSample(tex, tex_sampler, inputs.texCoord);
- }`,
+ }`
},
hookAliases: {
- 'getColor': ['filterColor'],
- },
+ getColor: ['filterColor']
+ }
}
);
}
@@ -4017,15 +4354,11 @@ ${hookUniformFields}}
baseComputeShader() {
if (!this._baseComputeShader) {
- this._baseComputeShader = new Shader(
- this,
- baseComputeShader,
- {
- compute: {
- 'void iteration': '(index: vec3) {}',
- },
+ this._baseComputeShader = new Shader(this, baseComputeShader, {
+ compute: {
+ 'void iteration': '(index: vec3) {}'
}
- );
+ });
}
return this._baseComputeShader;
}
@@ -4064,11 +4397,14 @@ ${hookUniformFields}}
size: {
width: size,
height: size,
- depthOrArrayLayers: 1,
+ depthOrArrayLayers: 1
},
mipLevelCount: mipLevels,
format: 'rgba8unorm',
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
+ usage:
+ GPUTextureUsage.TEXTURE_BINDING |
+ GPUTextureUsage.COPY_DST |
+ GPUTextureUsage.RENDER_ATTACHMENT
};
const gpuTexture = this.device.createTexture(textureDescriptor);
@@ -4095,17 +4431,17 @@ ${hookUniformFields}}
commandEncoder.copyTextureToTexture(
{
texture: sourceTexture,
- origin: { x: 0, y: 0, z: 0 },
+ origin: { x: 0, y: 0, z: 0 }
},
{
texture: mipmapData.gpuTexture,
mipLevel: mipLevel,
- origin: { x: 0, y: 0, z: 0 },
+ origin: { x: 0, y: 0, z: 0 }
},
{
width: width,
height: height,
- depthOrArrayLayers: 1,
+ depthOrArrayLayers: 1
}
);
@@ -4161,7 +4497,10 @@ ${hookUniformFields}}
let pz = z;
// we spread if we exceed GPU limits OR if it involves a large 1D dispatch
- const exceedsLimits = x > MAX_THREADS_PER_DIM || y > MAX_THREADS_PER_DIM || z > MAX_THREADS_PER_DIM;
+ const exceedsLimits =
+ x > MAX_THREADS_PER_DIM ||
+ y > MAX_THREADS_PER_DIM ||
+ z > MAX_THREADS_PER_DIM;
const isLarge1D = totalIterations > 1024 && y === 1 && z === 1;
if (exceedsLimits || isLarge1D) {
@@ -4183,11 +4522,15 @@ ${hookUniformFields}}
const passEncoder = commandEncoder.beginComputePass();
this.setupShaderBindGroups(shader, passEncoder, {
compute: true,
- workgroupSize: [WORKGROUP_SIZE_X, WORKGROUP_SIZE_Y, WORKGROUP_SIZE_Z],
+ workgroupSize: [WORKGROUP_SIZE_X, WORKGROUP_SIZE_Y, WORKGROUP_SIZE_Z]
});
// Dispatch compute workgroups
- passEncoder.dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ);
+ passEncoder.dispatchWorkgroups(
+ workgroupCountX,
+ workgroupCountY,
+ workgroupCountZ
+ );
passEncoder.end();
this.device.queue.submit([commandEncoder.finish()]);
@@ -4201,12 +4544,11 @@ ${hookUniformFields}}
// TODO: move this and the duplicate in the WebGL renderer to another file
fn.setAttributes = async function (key, value) {
return this._renderer._setAttributes(key, value);
- }
-
+ };
}
export default rendererWebGPU;
-if (typeof p5 !== "undefined") {
+if (typeof p5 !== 'undefined') {
rendererWebGPU(p5, p5.prototype);
}
diff --git a/src/webgpu/shaders/filters/base.js b/src/webgpu/shaders/filters/base.js
index d89704afed..4e8a99c34b 100644
--- a/src/webgpu/shaders/filters/base.js
+++ b/src/webgpu/shaders/filters/base.js
@@ -22,7 +22,9 @@ struct CameraUniforms {
@group(2) @binding(0) var camera: CameraUniforms;
`;
-export const baseFilterVertexShader = filterUniforms + `
+export const baseFilterVertexShader =
+ filterUniforms +
+ `
struct VertexInput {
@location(0) aPosition: vec3,
@location(1) aTexCoord: vec2,
@@ -50,7 +52,9 @@ fn main(input: VertexInput) -> VertexOutput {
}
`;
-export const baseFilterFragmentShader = filterUniforms + `
+export const baseFilterFragmentShader =
+ filterUniforms +
+ `
struct FilterInputs {
texCoord: vec2,
canvasSize: vec2,
diff --git a/src/webgpu/shaders/functions/noise3DWGSL.js b/src/webgpu/shaders/functions/noise3DWGSL.js
index 1fb96f5fc6..4749cc1256 100644
--- a/src/webgpu/shaders/functions/noise3DWGSL.js
+++ b/src/webgpu/shaders/functions/noise3DWGSL.js
@@ -103,4 +103,4 @@ fn noise(st: vec3, octaves: i32, ampFalloff: f32) -> f32 {
amplitude *= ampFalloff;
}
return (result + 1.0) * 0.5;
-}`;
\ No newline at end of file
+}`;
diff --git a/src/webgpu/shaders/line.js b/src/webgpu/shaders/line.js
index 2b58857286..7d8883708d 100644
--- a/src/webgpu/shaders/line.js
+++ b/src/webgpu/shaders/line.js
@@ -368,4 +368,3 @@ fn main(input: StrokeFragmentInput) -> @location(0) vec4 {
return vec4(col);
}
`;
-
diff --git a/src/webgpu/strands_wgslBackend.js b/src/webgpu/strands_wgslBackend.js
index 0ba1c29772..2d193f172f 100644
--- a/src/webgpu/strands_wgslBackend.js
+++ b/src/webgpu/strands_wgslBackend.js
@@ -2,8 +2,20 @@ import noiseWGSL from './shaders/functions/noise3DWGSL.js';
import randomWGSL from './shaders/functions/randomWGSL';
import randomVertWGSL from './shaders/functions/randomVertWGSL';
import randomComputeWGSL from './shaders/functions/randomComputeWGSL';
-import { NodeType, OpCodeToSymbol, BlockType, OpCode, NodeTypeToName, isStructType, BaseType, StatementType, DataType, INSTANCE_ID_VARYING_NAME, HOOK_PARAM_PREFIX } from "../strands/ir_types";
-import { getNodeDataFromID, extractNodeTypeInfo } from "../strands/ir_dag";
+import {
+ NodeType,
+ OpCodeToSymbol,
+ BlockType,
+ OpCode,
+ NodeTypeToName,
+ isStructType,
+ BaseType,
+ StatementType,
+ DataType,
+ INSTANCE_ID_VARYING_NAME,
+ HOOK_PARAM_PREFIX
+} from '../strands/ir_types';
+import { getNodeDataFromID, extractNodeTypeInfo } from '../strands/ir_dag';
import * as FES from '../strands/strands_FES';
import * as build from '../strands/ir_builders';
import { createStrandsNode } from '../strands/strands_node';
@@ -15,22 +27,22 @@ function shouldCreateTemp(dag, nodeID) {
return uses.length > 1;
}
const TypeNames = {
- 'float1': 'f32',
- 'float2': 'vec2',
- 'float3': 'vec3',
- 'float4': 'vec4',
- 'int1': 'i32',
- 'int2': 'vec2',
- 'int3': 'vec3',
- 'int4': 'vec4',
- 'bool1': 'bool',
- 'bool2': 'vec2',
- 'bool3': 'vec3',
- 'bool4': 'vec4',
- 'mat2': 'mat2x2',
- 'mat3': 'mat3x3',
- 'mat4': 'mat4x4',
-}
+ float1: 'f32',
+ float2: 'vec2',
+ float3: 'vec3',
+ float4: 'vec4',
+ int1: 'i32',
+ int2: 'vec2',
+ int3: 'vec3',
+ int4: 'vec4',
+ bool1: 'bool',
+ bool2: 'vec2',
+ bool3: 'vec3',
+ bool4: 'vec4',
+ mat2: 'mat2x2',
+ mat3: 'mat3x3',
+ mat4: 'mat4x4'
+};
const cfgHandlers = {
[BlockType.DEFAULT]: (blockID, strandsContext, generationContext) => {
const { dag, cfg } = strandsContext;
@@ -38,7 +50,11 @@ const cfgHandlers = {
for (const nodeID of instructions) {
const nodeType = dag.nodeTypes[nodeID];
if (shouldCreateTemp(dag, nodeID)) {
- const declaration = wgslBackend.generateDeclaration(generationContext, dag, nodeID);
+ const declaration = wgslBackend.generateDeclaration(
+ generationContext,
+ dag,
+ nodeID
+ );
generationContext.write(declaration);
}
if (nodeType === NodeType.STATEMENT) {
@@ -101,7 +117,11 @@ const cfgHandlers = {
[BlockType.IF_COND](blockID, strandsContext, generationContext) {
const { dag, cfg } = strandsContext;
const conditionID = cfg.blockConditions[blockID];
- const condExpr = wgslBackend.generateExpression(generationContext, dag, conditionID);
+ const condExpr = wgslBackend.generateExpression(
+ generationContext,
+ dag,
+ conditionID
+ );
generationContext.write(`if (${condExpr})`);
this[BlockType.DEFAULT](blockID, strandsContext, generationContext);
},
@@ -145,7 +165,11 @@ const cfgHandlers = {
generationContext.suppressSemicolon = isLast;
if (shouldCreateTemp(dag, nodeID)) {
- const declaration = wgslBackend.generateDeclaration(generationContext, dag, nodeID);
+ const declaration = wgslBackend.generateDeclaration(
+ generationContext,
+ dag,
+ nodeID
+ );
generationContext.write(declaration);
}
if (node.nodeType === NodeType.STATEMENT) {
@@ -177,29 +201,40 @@ const cfgHandlers = {
const sourceNodeID = node.dependsOn[branchIndex];
const tempName = generationContext.tempNames[nodeID];
if (tempName && sourceNodeID !== null) {
- const sourceExpr = wgslBackend.generateExpression(generationContext, dag, sourceNodeID);
+ const sourceExpr = wgslBackend.generateExpression(
+ generationContext,
+ dag,
+ sourceNodeID
+ );
generationContext.write(`${tempName} = ${sourceExpr};`);
}
}
}
}
}
- },
-}
+ }
+};
export const wgslBackend = {
hookEntry(hookType) {
- const params = hookType.parameters.map((param) => {
- // For struct types, use a raw prefix since we'll create a mutable copy
- const paramName = param.type.properties ? `_p5_strands_raw_${param.name}` : `${HOOK_PARAM_PREFIX}${param.name}`;
- return `${paramName}: ${param.type.typeName}`;
- }).join(', ');
+ const params = hookType.parameters
+ .map(param => {
+ // For struct types, use a raw prefix since we'll create a mutable copy
+ const paramName = param.type.properties
+ ? `_p5_strands_raw_${param.name}`
+ : `${HOOK_PARAM_PREFIX}${param.name}`;
+ return `${paramName}: ${param.type.typeName}`;
+ })
+ .join(', ');
const firstLine = `(${params}) {`;
// Generate mutable copies for struct parameters
const mutableCopies = hookType.parameters
.filter(param => param.type.properties) // Only struct types
- .map(param => ` var ${HOOK_PARAM_PREFIX}${param.name} = _p5_strands_raw_${param.name};`)
+ .map(
+ param =>
+ ` var ${HOOK_PARAM_PREFIX}${param.name} = _p5_strands_raw_${param.name};`
+ )
.join('\n');
return mutableCopies ? firstLine + '\n' + mutableCopies : firstLine;
@@ -211,10 +246,10 @@ export const wgslBackend = {
let bindingIndex = strandsContext.renderer.getNextBindingIndex({
vert: strandsContext.baseShader._vertSrc,
frag: strandsContext.baseShader._fragSrc,
- compute: strandsContext.baseShader._computeSrc,
+ compute: strandsContext.baseShader._computeSrc
});
- for (const {name, typeInfo} of strandsContext.uniforms) {
+ for (const { name, typeInfo } of strandsContext.uniforms) {
if (typeInfo.baseType === 'sampler2D') {
const textureBinding = `@group(0) @binding(${bindingIndex}) var ${name}: texture_2d;`;
const samplerBinding = `@group(0) @binding(${bindingIndex + 1}) var ${name}_sampler: sampler;`;
@@ -235,10 +270,10 @@ export const wgslBackend = {
let bindingIndex = strandsContext.renderer.getNextBindingIndex({
vert: strandsContext.baseShader._vertSrc,
frag: strandsContext.baseShader._fragSrc,
- compute: strandsContext.baseShader._computeSrc,
+ compute: strandsContext.baseShader._computeSrc
});
- for (const {name, typeInfo} of strandsContext.uniforms) {
+ for (const { name, typeInfo } of strandsContext.uniforms) {
if (typeInfo.baseType === 'storage') {
const accessMode = isComputeShader ? 'read_write' : 'read';
let declaration;
@@ -261,7 +296,7 @@ export const wgslBackend = {
}
},
getTypeName(baseType, dimension) {
- const primitiveTypeName = TypeNames[baseType + dimension]
+ const primitiveTypeName = TypeNames[baseType + dimension];
if (!primitiveTypeName) {
return baseType;
}
@@ -320,7 +355,11 @@ export const wgslBackend = {
} else if (node.statementType === StatementType.EARLY_RETURN) {
if (node.dependsOn && node.dependsOn.length > 0) {
const exprNodeID = node.dependsOn[0];
- const expr = this.generateExpression(generationContext, dag, exprNodeID);
+ const expr = this.generateExpression(
+ generationContext,
+ dag,
+ exprNodeID
+ );
generationContext.write(`return ${expr}${semicolon}`);
} else {
generationContext.write(`return${semicolon}`);
@@ -339,11 +378,27 @@ export const wgslBackend = {
// Check if target is an array access (storage buffer assignment)
if (targetNode.opCode === OpCode.Binary.ARRAY_ACCESS) {
const [bufferID, indexID] = targetNode.dependsOn;
- const bufferExpr = this.generateExpression(generationContext, dag, bufferID);
- const indexExpr = this.generateExpression(generationContext, dag, indexID);
- const sourceExpr = this.generateExpression(generationContext, dag, sourceNodeID);
- const fieldSuffix = targetNode.identifier ? `.${targetNode.identifier}` : '';
- generationContext.write(`${bufferExpr}[i32(${indexExpr})]${fieldSuffix} = ${sourceExpr}${semicolon}`);
+ const bufferExpr = this.generateExpression(
+ generationContext,
+ dag,
+ bufferID
+ );
+ const indexExpr = this.generateExpression(
+ generationContext,
+ dag,
+ indexID
+ );
+ const sourceExpr = this.generateExpression(
+ generationContext,
+ dag,
+ sourceNodeID
+ );
+ const fieldSuffix = targetNode.identifier
+ ? `.${targetNode.identifier}`
+ : '';
+ generationContext.write(
+ `${bufferExpr}[i32(${indexExpr})]${fieldSuffix} = ${sourceExpr}${semicolon}`
+ );
return;
}
@@ -351,10 +406,18 @@ export const wgslBackend = {
if (targetNode.opCode === OpCode.Unary.SWIZZLE) {
const parentID = targetNode.dependsOn[0];
const parentNode = getNodeDataFromID(dag, parentID);
- const parentExpr = this.generateExpression(generationContext, dag, parentID);
+ const parentExpr = this.generateExpression(
+ generationContext,
+ dag,
+ parentID
+ );
const swizzle = targetNode.swizzle;
const parentDimension = parentNode.dimension;
- const sourceExpr = this.generateExpression(generationContext, dag, sourceNodeID);
+ const sourceExpr = this.generateExpression(
+ generationContext,
+ dag,
+ sourceNodeID
+ );
// Create an array for each element of the target variable
const componentMap = [];
@@ -363,7 +426,7 @@ export const wgslBackend = {
}
// Map swizzle characters to component indices
- const getComponentIndex = (char) => {
+ const getComponentIndex = char => {
if ('xyzw'.includes(char)) return 'xyzw'.indexOf(char);
if ('rgba'.includes(char)) return 'rgba'.indexOf(char);
return -1;
@@ -372,22 +435,38 @@ export const wgslBackend = {
// Update the component map based on the swizzle assignment
for (let i = 0; i < swizzle.length; i++) {
const targetComponentIndex = getComponentIndex(swizzle[i]);
- if (targetComponentIndex >= 0 && targetComponentIndex < parentDimension) {
+ if (
+ targetComponentIndex >= 0 &&
+ targetComponentIndex < parentDimension
+ ) {
componentMap[targetComponentIndex] = { target: 'rhs', index: i };
}
}
// Generate the reconstruction expression
- const vectorTypeName = this.getTypeName(parentNode.baseType, parentDimension);
+ const vectorTypeName = this.getTypeName(
+ parentNode.baseType,
+ parentDimension
+ );
const components = componentMap.map(({ target, index }) => {
- return `${target === 'self' ? parentExpr : sourceExpr}.${'xyzw'[index]}`
+ return `${target === 'self' ? parentExpr : sourceExpr}.${'xyzw'[index]}`;
});
- generationContext.write(`${parentExpr} = ${vectorTypeName}(${components.join(', ')})${semicolon}`);
+ generationContext.write(
+ `${parentExpr} = ${vectorTypeName}(${components.join(', ')})${semicolon}`
+ );
} else {
// Regular assignment
- const targetExpr = this.generateExpression(generationContext, dag, targetNodeID);
- const sourceExpr = this.generateExpression(generationContext, dag, sourceNodeID);
+ const targetExpr = this.generateExpression(
+ generationContext,
+ dag,
+ targetNodeID
+ );
+ const sourceExpr = this.generateExpression(
+ generationContext,
+ dag,
+ sourceNodeID
+ );
// Generate assignment if we have both target and source
if (targetExpr && sourceExpr && targetExpr !== sourceExpr) {
@@ -403,7 +482,12 @@ export const wgslBackend = {
const typeName = this.getTypeName(T.baseType, T.dimension);
return `var ${tmp}: ${typeName} = ${expr};`;
},
- generateReturnStatement(strandsContext, generationContext, rootNodeID, returnType) {
+ generateReturnStatement(
+ strandsContext,
+ generationContext,
+ rootNodeID,
+ returnType
+ ) {
if (!returnType) {
generationContext.write('return;');
return;
@@ -414,15 +498,21 @@ export const wgslBackend = {
const structTypeInfo = returnType;
for (let i = 0; i < structTypeInfo.properties.length; i++) {
const prop = structTypeInfo.properties[i];
- const val = this.generateExpression(generationContext, dag, rootNode.dependsOn[i]);
+ const val = this.generateExpression(
+ generationContext,
+ dag,
+ rootNode.dependsOn[i]
+ );
if (prop.name !== val) {
generationContext.write(
`${rootNode.identifier}.${prop.name} = ${val};`
- )
+ );
}
}
}
- generationContext.write(`return ${this.generateExpression(generationContext, dag, rootNodeID)};`);
+ generationContext.write(
+ `return ${this.generateExpression(generationContext, dag, rootNodeID)};`
+ );
},
generateExpression(generationContext, dag, nodeID) {
const node = getNodeDataFromID(dag, nodeID);
@@ -431,160 +521,221 @@ export const wgslBackend = {
}
switch (node.nodeType) {
case NodeType.LITERAL:
- if (node.baseType === BaseType.FLOAT) {
- return node.value.toFixed(4);
- }
- else {
- return node.value;
- }
+ if (node.baseType === BaseType.FLOAT) {
+ return node.value.toFixed(4);
+ } else {
+ return node.value;
+ }
case NodeType.VARIABLE:
- // Track shared variable usage context
- if (generationContext.shaderContext && generationContext.strandsContext?.sharedVariables?.has(node.identifier)) {
- const sharedVar = generationContext.strandsContext.sharedVariables.get(node.identifier);
- if (generationContext.shaderContext === 'vertex') {
- sharedVar.usedInVertex = true;
- } else if (generationContext.shaderContext === 'fragment') {
- sharedVar.usedInFragment = true;
+ // Track shared variable usage context
+ if (
+ generationContext.shaderContext &&
+ generationContext.strandsContext?.sharedVariables?.has(
+ node.identifier
+ )
+ ) {
+ const sharedVar =
+ generationContext.strandsContext.sharedVariables.get(
+ node.identifier
+ );
+ if (generationContext.shaderContext === 'vertex') {
+ sharedVar.usedInVertex = true;
+ } else if (generationContext.shaderContext === 'fragment') {
+ sharedVar.usedInFragment = true;
+ }
}
- }
- // Detect instanceID usage in fragment context and rewrite to varying name
- if (node.identifier === this.instanceIdReference() && generationContext.shaderContext === 'fragment') {
- generationContext.strandsContext._instanceIDUsedInFragment = true;
- return INSTANCE_ID_VARYING_NAME;
- }
+ // Detect instanceID usage in fragment context and rewrite to varying name
+ if (
+ node.identifier === this.instanceIdReference() &&
+ generationContext.shaderContext === 'fragment'
+ ) {
+ generationContext.strandsContext._instanceIDUsedInFragment = true;
+ return INSTANCE_ID_VARYING_NAME;
+ }
- // Check if this is a uniform variable (but not a texture or storage buffer)
- const uniform = generationContext.strandsContext?.uniforms?.find(uniform => uniform.name === node.identifier);
- if (uniform && uniform.typeInfo.baseType !== 'sampler2D' && uniform.typeInfo.baseType !== 'storage') {
- return `hooks.${node.identifier}`;
- }
+ // Check if this is a uniform variable (but not a texture or storage buffer)
+ const uniform = generationContext.strandsContext?.uniforms?.find(
+ uniform => uniform.name === node.identifier
+ );
+ if (
+ uniform &&
+ uniform.typeInfo.baseType !== 'sampler2D' &&
+ uniform.typeInfo.baseType !== 'storage'
+ ) {
+ return `hooks.${node.identifier}`;
+ }
- return node.identifier;
+ return node.identifier;
case NodeType.OPERATION:
- const useParantheses = node.usedBy.length > 0;
- if (node.opCode === OpCode.Nary.CONSTRUCTOR) {
- // TODO: differentiate casts and constructors for more efficient codegen.
- // if (node.dependsOn.length === 1 && node.dimension === 1) {
- // return this.generateExpression(generationContext, dag, node.dependsOn[0]);
- // }
- if (node.baseType === BaseType.SAMPLER2D) {
- return this.generateExpression(generationContext, dag, node.dependsOn[0]);
- }
- const T = this.getTypeName(node.baseType, node.dimension);
- const deps = node.dependsOn.map((dep) => this.generateExpression(generationContext, dag, dep));
- return `${T}(${deps.join(', ')})`;
- }
- if (node.opCode === OpCode.Nary.TERNARY) {
- const [condID, trueID, falseID] = node.dependsOn;
- const cond = this.generateExpression(generationContext, dag, condID);
- const trueExpr = this.generateExpression(generationContext, dag, trueID);
- const falseExpr = this.generateExpression(generationContext, dag, falseID);
- return `select(${falseExpr}, ${trueExpr}, ${cond})`;
- }
- if (node.opCode === OpCode.Nary.FUNCTION_CALL) {
- // Convert mod() function calls to % operator in WGSL
- if (node.identifier === 'mod' && node.dependsOn.length === 2) {
- const [leftID, rightID] = node.dependsOn;
- const left = this.generateExpression(generationContext, dag, leftID);
- const right = this.generateExpression(generationContext, dag, rightID);
- const useParantheses = node.usedBy.length > 0;
- if (useParantheses) {
- return `(${left} % ${right})`;
- } else {
- return `${left} % ${right}`;
+ const useParantheses = node.usedBy.length > 0;
+ if (node.opCode === OpCode.Nary.CONSTRUCTOR) {
+ // TODO: differentiate casts and constructors for more efficient codegen.
+ // if (node.dependsOn.length === 1 && node.dimension === 1) {
+ // return this.generateExpression(generationContext, dag, node.dependsOn[0]);
+ // }
+ if (node.baseType === BaseType.SAMPLER2D) {
+ return this.generateExpression(
+ generationContext,
+ dag,
+ node.dependsOn[0]
+ );
}
+ const T = this.getTypeName(node.baseType, node.dimension);
+ const deps = node.dependsOn.map(dep =>
+ this.generateExpression(generationContext, dag, dep)
+ );
+ return `${T}(${deps.join(', ')})`;
}
-
- // Convert atan(y, x) to atan2(y, x) in WGSL
- if (node.identifier === 'atan' && node.dependsOn.length === 2) {
- const functionArgs = node.dependsOn.map(arg => this.generateExpression(generationContext, dag, arg));
- return `atan2(${functionArgs.join(', ')})`;
+ if (node.opCode === OpCode.Nary.TERNARY) {
+ const [condID, trueID, falseID] = node.dependsOn;
+ const cond = this.generateExpression(generationContext, dag, condID);
+ const trueExpr = this.generateExpression(
+ generationContext,
+ dag,
+ trueID
+ );
+ const falseExpr = this.generateExpression(
+ generationContext,
+ dag,
+ falseID
+ );
+ return `select(${falseExpr}, ${trueExpr}, ${cond})`;
}
+ if (node.opCode === OpCode.Nary.FUNCTION_CALL) {
+ // Convert mod() function calls to % operator in WGSL
+ if (node.identifier === 'mod' && node.dependsOn.length === 2) {
+ const [leftID, rightID] = node.dependsOn;
+ const left = this.generateExpression(
+ generationContext,
+ dag,
+ leftID
+ );
+ const right = this.generateExpression(
+ generationContext,
+ dag,
+ rightID
+ );
+ const useParantheses = node.usedBy.length > 0;
+ if (useParantheses) {
+ return `(${left} % ${right})`;
+ } else {
+ return `${left} % ${right}`;
+ }
+ }
- const functionArgs = node.dependsOn.map(arg =>this.generateExpression(generationContext, dag, arg));
+ // Convert atan(y, x) to atan2(y, x) in WGSL
+ if (node.identifier === 'atan' && node.dependsOn.length === 2) {
+ const functionArgs = node.dependsOn.map(arg =>
+ this.generateExpression(generationContext, dag, arg)
+ );
+ return `atan2(${functionArgs.join(', ')})`;
+ }
- if (node.identifier === 'random') {
- const ctx = generationContext.shaderContext;
- if (ctx === 'fragment') {
- functionArgs.push('_p5FragPos.xy');
- } else if (ctx === 'vertex') {
- functionArgs.push('f32(_p5VertexId)');
- } else if (ctx === 'compute') {
- functionArgs.push('_p5GlobalId');
+ const functionArgs = node.dependsOn.map(arg =>
+ this.generateExpression(generationContext, dag, arg)
+ );
+
+ if (node.identifier === 'random') {
+ const ctx = generationContext.shaderContext;
+ if (ctx === 'fragment') {
+ functionArgs.push('_p5FragPos.xy');
+ } else if (ctx === 'vertex') {
+ functionArgs.push('f32(_p5VertexId)');
+ } else if (ctx === 'compute') {
+ functionArgs.push('_p5GlobalId');
+ }
}
- }
- return `${node.identifier}(${functionArgs.join(', ')})`;
- }
- if (node.opCode === OpCode.Binary.MEMBER_ACCESS) {
- const [lID, rID] = node.dependsOn;
- const lName = this.generateExpression(generationContext, dag, lID);
- const rName = this.generateExpression(generationContext, dag, rID);
- return `${lName}.${rName}`;
- }
- if (node.opCode === OpCode.Unary.SWIZZLE) {
- const parentID = node.dependsOn[0];
- const parentExpr = this.generateExpression(generationContext, dag, parentID);
- return `${parentExpr}.${node.swizzle}`;
- }
- if (node.opCode === OpCode.Binary.ARRAY_ACCESS) {
- const [bufferID, indexID] = node.dependsOn;
- const bufferExpr = this.generateExpression(generationContext, dag, bufferID);
- const indexExpr = this.generateExpression(generationContext, dag, indexID);
- const fieldSuffix = node.identifier ? `.${node.identifier}` : '';
- return `${bufferExpr}[i32(${indexExpr})]${fieldSuffix}`;
- }
- if (node.dependsOn.length === 2) {
- const [lID, rID] = node.dependsOn;
- const left = this.generateExpression(generationContext, dag, lID);
- const right = this.generateExpression(generationContext, dag, rID);
-
- // In WGSL, % operator works for both floats and integers
- if (node.opCode === OpCode.Binary.MODULO) {
- return `(${left} % ${right})`;
+ return `${node.identifier}(${functionArgs.join(', ')})`;
+ }
+ if (node.opCode === OpCode.Binary.MEMBER_ACCESS) {
+ const [lID, rID] = node.dependsOn;
+ const lName = this.generateExpression(generationContext, dag, lID);
+ const rName = this.generateExpression(generationContext, dag, rID);
+ return `${lName}.${rName}`;
+ }
+ if (node.opCode === OpCode.Unary.SWIZZLE) {
+ const parentID = node.dependsOn[0];
+ const parentExpr = this.generateExpression(
+ generationContext,
+ dag,
+ parentID
+ );
+ return `${parentExpr}.${node.swizzle}`;
}
+ if (node.opCode === OpCode.Binary.ARRAY_ACCESS) {
+ const [bufferID, indexID] = node.dependsOn;
+ const bufferExpr = this.generateExpression(
+ generationContext,
+ dag,
+ bufferID
+ );
+ const indexExpr = this.generateExpression(
+ generationContext,
+ dag,
+ indexID
+ );
+ const fieldSuffix = node.identifier ? `.${node.identifier}` : '';
+ return `${bufferExpr}[i32(${indexExpr})]${fieldSuffix}`;
+ }
+ if (node.dependsOn.length === 2) {
+ const [lID, rID] = node.dependsOn;
+ const left = this.generateExpression(generationContext, dag, lID);
+ const right = this.generateExpression(generationContext, dag, rID);
- const opSym = OpCodeToSymbol[node.opCode];
- if (useParantheses) {
- return `(${left} ${opSym} ${right})`;
- } else {
- return `${left} ${opSym} ${right}`;
+ // In WGSL, % operator works for both floats and integers
+ if (node.opCode === OpCode.Binary.MODULO) {
+ return `(${left} % ${right})`;
+ }
+
+ const opSym = OpCodeToSymbol[node.opCode];
+ if (useParantheses) {
+ return `(${left} ${opSym} ${right})`;
+ } else {
+ return `${left} ${opSym} ${right}`;
+ }
}
- }
- if (node.opCode === OpCode.Unary.LOGICAL_NOT
- || node.opCode === OpCode.Unary.NEGATE
- || node.opCode === OpCode.Unary.PLUS
+ if (
+ node.opCode === OpCode.Unary.LOGICAL_NOT ||
+ node.opCode === OpCode.Unary.NEGATE ||
+ node.opCode === OpCode.Unary.PLUS
) {
- const [i] = node.dependsOn;
- const val = this.generateExpression(generationContext, dag, i);
- const sym = OpCodeToSymbol[node.opCode];
- return `${sym}${val}`;
- }
+ const [i] = node.dependsOn;
+ const val = this.generateExpression(generationContext, dag, i);
+ const sym = OpCodeToSymbol[node.opCode];
+ return `${sym}${val}`;
+ }
case NodeType.PHI:
- // Phi nodes represent conditional merging of values
- // If this phi node has an identifier (like varying variables), use that
- if (node.identifier) {
- return node.identifier;
- }
- // Otherwise, they should have been declared as temporary variables
- // and assigned in the appropriate branches
- if (generationContext.tempNames?.[nodeID]) {
- return generationContext.tempNames[nodeID];
- } else {
- // If no temp was created, this phi node only has one input
- // so we can just use that directly
- const validInputs = node.dependsOn.filter(id => id !== null);
- if (validInputs.length > 0) {
- return this.generateExpression(generationContext, dag, validInputs[0]);
+ // Phi nodes represent conditional merging of values
+ // If this phi node has an identifier (like varying variables), use that
+ if (node.identifier) {
+ return node.identifier;
+ }
+ // Otherwise, they should have been declared as temporary variables
+ // and assigned in the appropriate branches
+ if (generationContext.tempNames?.[nodeID]) {
+ return generationContext.tempNames[nodeID];
} else {
- throw new Error('No valid inputs for node');
+ // If no temp was created, this phi node only has one input
+ // so we can just use that directly
+ const validInputs = node.dependsOn.filter(id => id !== null);
+ if (validInputs.length > 0) {
+ return this.generateExpression(
+ generationContext,
+ dag,
+ validInputs[0]
+ );
+ } else {
+ throw new Error('No valid inputs for node');
+ }
}
- }
case NodeType.ASSIGNMENT:
- FES.internalError(`ASSIGNMENT nodes should not be used as expressions`)
+ FES.internalError(`ASSIGNMENT nodes should not be used as expressions`);
default:
- FES.internalError(`${NodeTypeToName[node.nodeType]} code generation not implemented yet`)
+ FES.internalError(
+ `${NodeTypeToName[node.nodeType]} code generation not implemented yet`
+ );
}
},
generateBlock(blockID, strandsContext, generationContext) {
@@ -605,8 +756,16 @@ export const wgslBackend = {
const textureNode = getNodeDataFromID(dag, textureArg.id);
const samplerIdentifier = textureNode.identifier + '_sampler';
- const samplerVariable = build.variableNode(strandsContext, { baseType: BaseType.SAMPLER, dimension: 1 }, samplerIdentifier);
- const samplerNode = createStrandsNode(samplerVariable.id, samplerVariable.dimension, strandsContext);
+ const samplerVariable = build.variableNode(
+ strandsContext,
+ { baseType: BaseType.SAMPLER, dimension: 1 },
+ samplerIdentifier
+ );
+ const samplerNode = createStrandsNode(
+ samplerVariable.id,
+ samplerVariable.dimension,
+ strandsContext
+ );
// Create a LOD literal node (0.0) so we can use textureSampleLevel instead
// of textureSample. textureSample doesn't let you use uniform values in control
@@ -619,17 +778,33 @@ export const wgslBackend = {
{ dimension: 1, baseType: BaseType.FLOAT },
0.0
);
- const lodNode = createStrandsNode(lodLiteral.id, lodLiteral.dimension, strandsContext);
+ const lodNode = createStrandsNode(
+ lodLiteral.id,
+ lodLiteral.dimension,
+ strandsContext
+ );
// Create the augmented args: [texture, sampler, coords, lod]
const augmentedArgs = [textureArg, samplerNode, coordsArg, lodNode];
- const { id, dimension } = build.functionCallNode(strandsContext, 'textureSampleLevel', augmentedArgs, {
- overloads: [{
- params: [DataType.sampler2D, DataType.sampler, DataType.float2, DataType.float1],
- returnType: DataType.float4
- }]
- });
+ const { id, dimension } = build.functionCallNode(
+ strandsContext,
+ 'textureSampleLevel',
+ augmentedArgs,
+ {
+ overloads: [
+ {
+ params: [
+ DataType.sampler2D,
+ DataType.sampler,
+ DataType.float2,
+ DataType.float1
+ ],
+ returnType: DataType.float4
+ }
+ ]
+ }
+ );
return { id, dimension };
},
@@ -638,6 +813,11 @@ export const wgslBackend = {
},
generateInstanceIDVarying() {
- return { name: INSTANCE_ID_VARYING_NAME, declaration: `${INSTANCE_ID_VARYING_NAME}: i32`, source: 'i32(instanceID)', interpolation: 'flat' };
- },
-}
+ return {
+ name: INSTANCE_ID_VARYING_NAME,
+ declaration: `${INSTANCE_ID_VARYING_NAME}: i32`,
+ source: 'i32(instanceID)',
+ interpolation: 'flat'
+ };
+ }
+};
diff --git a/stewards.yml b/stewards.yml
index 5b23c9133a..e5340c8eac 100644
--- a/stewards.yml
+++ b/stewards.yml
@@ -1,4 +1,3 @@
-
calebfoss:
- Accessibility
@@ -51,7 +50,7 @@ clairep94:
Nwakaego-Ego:
- p5.js-website
-
+
doradocodes:
- p5.js-website
- Maintainers
@@ -59,7 +58,7 @@ doradocodes:
coseeian:
- Accessibility:
- p5.js-website
-
+
marioguzzzman:
- i18n:
- es
diff --git a/test/bench/REPORTS/REPORT_1_TEST CPU.md b/test/bench/REPORTS/REPORT_1_TEST CPU.md
index f645c2f235..22922122b4 100644
--- a/test/bench/REPORTS/REPORT_1_TEST CPU.md
+++ b/test/bench/REPORTS/REPORT_1_TEST CPU.md
@@ -33,7 +33,7 @@ function setup() {
function draw() {
background(220);
-
+
for (const s of state) {
s.pos.add(s.vel)
for (const axis of ['x', 'y']) {
@@ -47,7 +47,7 @@ function draw() {
}
}
}
-
+
const drawCircles = () => {
for (const s of state) {
push()
@@ -71,64 +71,79 @@ function draw() {
} else {
drawCircles()
}
-
+
fps.html(round(frameRate()))
}
```
-
### test/bench/dave.bench.js (2) 35272ms
+
- **Dave bench test (2) 35268ms**
- **TEST CPU TRANSFORMS true**: 0.7533 ops/sec ±3.80% (10 samples)
- **TEST CPU TRANSFORMS false**: 0.9418 ops/sec ±0.20% (10 samples) _fastest_
#### Summary
+
- **TEST CPU TRANSFORMS false** is 1.25x faster than **TEST CPU TRANSFORMS true**
### Separated
+
#### test/bench/dave.bench.js (2) 27912ms
+
- **Dave bench test (2) 27909ms**
- **TEST CPU TRANSFORMS false**: 0.5350 ops/sec ±4.39% (10 samples)
- **TEST CPU TRANSFORMS true**: _skipped_
#### test/bench/dave.bench.js (2) 19331ms
+
- **Dave bench test (2) 19327ms**
- **TEST CPU TRANSFORMS false**: _skipped_
- **TEST CPU TRANSFORMS true**: 0.7519 ops/sec ±3.80% (10 samples)
### Comparing
+
#### DEV v2.1.5 /Users/cristianbanuelos/repos/p5.js.git/bench
+
- **test/bench/dave.bench.js (2) 15671ms**
- **Dave bench test (2) 15667ms**
- **TEST CPU TRANSFORMS false**: 0.9719 ops/sec ±1.36% (2 samples) _fastest_
- **TEST CPU TRANSFORMS true**: 0.8170 ops/sec ±15.16% (2 samples)
#### Summary
+
- **TEST CPU TRANSFORMS false** is 1.19x faster than **TEST CPU TRANSFORMS true**
#### DEV v2.1.5 /Users/cristianbanuelos/repos/p5.js.git/bench
+
- **test/bench/dave.bench.js (2) 15915ms**
- **Dave bench test (2) 15905ms**
- **TEST CPU TRANSFORMS true**: 0.7961 ops/sec ±4.86% (2 samples)
- **TEST CPU TRANSFORMS false**: 0.9286 ops/sec ±36.88% (2 samples) _fastest_
#### Summary
+
- **TEST CPU TRANSFORMS false** is 1.17x faster than **TEST CPU TRANSFORMS true**
## With 1000 Elements
+
### test/bench/dave.bench.js (2) 151509ms
+
- **Dave bench test (2) 151505ms**
- **TEST CPU TRANSFORMS true**: 0.0582 ops/sec ±0.00% (1 sample)
- **TEST CPU TRANSFORMS false**: 0.0912 ops/sec ±0.00% (1 sample) _fastest_
#### Summary
+
- **TEST CPU TRANSFORMS false** is 1.57x faster than **TEST CPU TRANSFORMS true**
### 1000 Elements Second Run
+
#### test/bench/dave.bench.js (2) 150601ms
+
- **Dave bench test (2) 150597ms**
- **TEST CPU TRANSFORMS true**: 0.0589 ops/sec ±0.00% (1 sample)
- **TEST CPU TRANSFORMS false**: 0.0914 ops/sec ±0.00% (1 sample) _fastest_
#### Summary
+
- **TEST CPU TRANSFORMS false** is 1.55x faster than **TEST CPU TRANSFORMS true**
diff --git a/test/bench/cpu_transforms.bench.js b/test/bench/cpu_transforms.bench.js
index f94b322842..edfd7aeffd 100644
--- a/test/bench/cpu_transforms.bench.js
+++ b/test/bench/cpu_transforms.bench.js
@@ -1,12 +1,12 @@
// import p5 from "../../../src/app.js";
-import { bench, describe } from "vitest";
-import p5 from "../../src/app";
+import { bench, describe } from 'vitest';
+import p5 from '../../src/app';
const options = { iterations: 1, time: 1500 };
-const ITERATIONS = 100
-describe.sequential("Dave bench test", () => {
- bench(
- "TEST CPU TRANSFORMS true",
+const ITERATIONS = 100;
+describe.sequential('Dave bench test', () => {
+ (bench(
+ 'TEST CPU TRANSFORMS true',
async () => {
try {
const TEST_CPU_TRANSFORMS = true;
@@ -18,7 +18,7 @@ describe.sequential("Dave bench test", () => {
});
await vi.waitFor(() => {
if (myp5 === undefined) {
- throw new Error("not ready");
+ throw new Error('not ready');
}
});
let fps;
@@ -31,7 +31,7 @@ describe.sequential("Dave bench test", () => {
myp5.random(-200, 200),
myp5.random(-200, 200)
),
- vel: myp5.createVector(myp5.random(-2, 2), myp5.random(-2, 2)),
+ vel: myp5.createVector(myp5.random(-2, 2), myp5.random(-2, 2))
});
}
fps = myp5.createP();
@@ -44,7 +44,7 @@ describe.sequential("Dave bench test", () => {
for (const s of state) {
s.pos.add(s.vel);
- for (const axis of ["x", "y"]) {
+ for (const axis of ['x', 'y']) {
if (s.pos[axis] < -200) {
s.pos[axis] = -200;
s.vel[axis] *= -1;
@@ -86,9 +86,9 @@ describe.sequential("Dave bench test", () => {
},
options
),
- options;
- bench(
- "TEST CPU TRANSFORMS false",
+ options);
+ (bench(
+ 'TEST CPU TRANSFORMS false',
async () => {
const TEST_CPU_TRANSFORMS = false;
var myp5;
@@ -99,7 +99,7 @@ describe.sequential("Dave bench test", () => {
});
await vi.waitFor(() => {
if (myp5 === undefined) {
- throw new Error("not ready");
+ throw new Error('not ready');
}
});
let fps;
@@ -112,7 +112,7 @@ describe.sequential("Dave bench test", () => {
myp5.random(-200, 200),
myp5.random(-200, 200)
),
- vel: myp5.createVector(myp5.random(-2, 2), myp5.random(-2, 2)),
+ vel: myp5.createVector(myp5.random(-2, 2), myp5.random(-2, 2))
});
}
fps = myp5.createP();
@@ -125,7 +125,7 @@ describe.sequential("Dave bench test", () => {
for (const s of state) {
s.pos.add(s.vel);
- for (const axis of ["x", "y"]) {
+ for (const axis of ['x', 'y']) {
if (s.pos[axis] < -200) {
s.pos[axis] = -200;
s.vel[axis] *= -1;
@@ -164,5 +164,5 @@ describe.sequential("Dave bench test", () => {
},
options
),
- options;
+ options);
});
diff --git a/test/bench/poc.bench.js b/test/bench/poc.bench.js
index 100cd2f197..6ee7e901d0 100644
--- a/test/bench/poc.bench.js
+++ b/test/bench/poc.bench.js
@@ -1,20 +1,20 @@
-import { bench, describe } from "vitest";
+import { bench, describe } from 'vitest';
-describe.only("sort", () => {
+describe.only('sort', () => {
bench(
- "normal",
+ 'normal',
() => {
const x = [1, 5, 4, 2, 3];
x.sort((a, b) => {
return a - b;
});
- throw new Error("error");
+ throw new Error('error');
},
{ iterations: 100 }
);
bench(
- "reverse",
+ 'reverse',
() => {
const x = [1, 5, 4, 2, 3];
x.reverse()
diff --git a/test/bench/rendering.bench.js b/test/bench/rendering.bench.js
index 7d4813473f..c79c27653b 100644
--- a/test/bench/rendering.bench.js
+++ b/test/bench/rendering.bench.js
@@ -1,11 +1,11 @@
// import p5 from "../../../src/app.js";
-import { bench, describe } from "vitest";
-import p5 from "../../src/app";
+import { bench, describe } from 'vitest';
+import p5 from '../../src/app';
const options = { iterations: 20, time: 500 };
-describe("Rendering bench test", () => {
+describe('Rendering bench test', () => {
bench(
- "normal",
+ 'normal',
async () => {
try {
var myp5;
@@ -17,7 +17,7 @@ describe("Rendering bench test", () => {
});
await vi.waitFor(() => {
if (myp5 === undefined) {
- throw new Error("not ready");
+ throw new Error('not ready');
}
});
@@ -35,7 +35,7 @@ describe("Rendering bench test", () => {
);
bench(
- "thousand",
+ 'thousand',
async () => {
try {
var myp5;
@@ -47,7 +47,7 @@ describe("Rendering bench test", () => {
});
await vi.waitFor(() => {
if (myp5 === undefined) {
- throw new Error("not ready");
+ throw new Error('not ready');
}
});
@@ -68,7 +68,7 @@ describe("Rendering bench test", () => {
);
bench(
- "10k",
+ '10k',
async () => {
try {
var myp5;
@@ -80,7 +80,7 @@ describe("Rendering bench test", () => {
});
await vi.waitFor(() => {
if (myp5 === undefined) {
- throw new Error("not ready");
+ throw new Error('not ready');
}
});
@@ -101,9 +101,9 @@ describe("Rendering bench test", () => {
);
});
-describe("Another suite", () => {
+describe('Another suite', () => {
bench(
- "normal v2",
+ 'normal v2',
async () => {
try {
var myp5;
@@ -115,7 +115,7 @@ describe("Another suite", () => {
});
await vi.waitFor(() => {
if (myp5 === undefined) {
- throw new Error("not ready");
+ throw new Error('not ready');
}
});
@@ -133,7 +133,7 @@ describe("Another suite", () => {
);
bench(
- "thousand v2",
+ 'thousand v2',
async () => {
try {
var myp5;
@@ -145,7 +145,7 @@ describe("Another suite", () => {
});
await vi.waitFor(() => {
if (myp5 === undefined) {
- throw new Error("not ready");
+ throw new Error('not ready');
}
});
@@ -166,7 +166,7 @@ describe("Another suite", () => {
);
bench(
- "10k v2",
+ '10k v2',
async () => {
try {
var myp5;
@@ -178,7 +178,7 @@ describe("Another suite", () => {
});
await vi.waitFor(() => {
if (myp5 === undefined) {
- throw new Error("not ready");
+ throw new Error('not ready');
}
});
diff --git a/test/bench/vectors.bench.js b/test/bench/vectors.bench.js
index 952c428509..051e4710a2 100644
--- a/test/bench/vectors.bench.js
+++ b/test/bench/vectors.bench.js
@@ -1,120 +1,92 @@
import { Vector } from '../../src/math/p5.Vector.js';
-import { bench, describe } from "vitest";
+import { bench, describe } from 'vitest';
+describe('vector operations', () => {
+ bench('mult 5', () => {
+ const nLimited = 5;
+ // TODO try just operating on det. values based on i and j
+ // without re-creation
-describe("vector operations", () => {
-
- bench(
- "mult 5",
- () => {
- const nLimited = 5;
- // TODO try just operating on det. values based on i and j
- // without re-creation
-
- for (let i = 0; i < nLimited; i++) {
- for (let j = 0; j < nLimited; j++) {
- const tmp = arr[i].mult(arr[j]);
- }
+ for (let i = 0; i < nLimited; i++) {
+ for (let j = 0; j < nLimited; j++) {
+ const tmp = arr[i].mult(arr[j]);
}
}
- );
+ });
+
+ bench('mult 10', () => {
+ const nLimited = 10;
+ const arr = setupVectors();
- bench(
- "mult 10",
- () => {
- const nLimited = 10;
- const arr = setupVectors();
-
- for (let i = 0; i < nLimited; i++) {
- for (let j = 0; j < nLimited; j++) {
- const tmp = arr[i].mult(arr[j]);
- }
+ for (let i = 0; i < nLimited; i++) {
+ for (let j = 0; j < nLimited; j++) {
+ const tmp = arr[i].mult(arr[j]);
}
}
- );
+ });
+
+ bench('mult 20', () => {
+ const nLimited = 20;
+ const arr = setupVectors();
- bench(
- "mult 20",
- () => {
- const nLimited = 20;
- const arr = setupVectors();
-
- for (let i = 0; i < nLimited; i++) {
- for (let j = 0; j < nLimited; j++) {
- const tmp = arr[i].mult(arr[j]);
- }
+ for (let i = 0; i < nLimited; i++) {
+ for (let j = 0; j < nLimited; j++) {
+ const tmp = arr[i].mult(arr[j]);
}
}
- );
- bench(
- "mult 100",
- () => {
- const nLimited = 100;
- const arr = setupVectors();
-
- for (let i = 0; i < nLimited; i++) {
- for (let j = 0; j < nLimited; j++) {
- const tmp = arr[i].mult(arr[j]);
- }
+ });
+ bench('mult 100', () => {
+ const nLimited = 100;
+ const arr = setupVectors();
+
+ for (let i = 0; i < nLimited; i++) {
+ for (let j = 0; j < nLimited; j++) {
+ const tmp = arr[i].mult(arr[j]);
}
}
- );
-
+ });
+ bench('add 5', () => {
+ const nLimited = 5;
+ const arr = setupVectors();
- bench(
- "add 5",
- () => {
- const nLimited = 5;
- const arr = setupVectors();
-
- for (let i = 0; i < nLimited; i++) {
- for (let j = 0; j < nLimited; j++) {
- const tmp = arr[i].add(arr[j]);
- }
+ for (let i = 0; i < nLimited; i++) {
+ for (let j = 0; j < nLimited; j++) {
+ const tmp = arr[i].add(arr[j]);
}
}
- );
+ });
- bench(
- "add 10",
- () => {
- const nLimited = 10;
- const arr = setupVectors();
-
- for (let i = 0; i < nLimited; i++) {
- for (let j = 0; j < nLimited; j++) {
- const tmp = arr[i].add(arr[j]);
- }
+ bench('add 10', () => {
+ const nLimited = 10;
+ const arr = setupVectors();
+
+ for (let i = 0; i < nLimited; i++) {
+ for (let j = 0; j < nLimited; j++) {
+ const tmp = arr[i].add(arr[j]);
}
- });
+ }
+ });
+
+ bench('add 20', () => {
+ const nLimited = 20;
+ const arr = setupVectors();
- bench(
- "add 20",
- () => {
- const nLimited = 20;
- const arr = setupVectors();
-
- for (let i = 0; i < nLimited; i++) {
- for (let j = 0; j < nLimited; j++) {
- const tmp = arr[i].add(arr[j]);
- }
+ for (let i = 0; i < nLimited; i++) {
+ for (let j = 0; j < nLimited; j++) {
+ const tmp = arr[i].add(arr[j]);
}
}
- );
- bench(
- "add 100",
- () => {
- const nLimited = 100;
- const arr = setupVectors();
-
- for (let i = 0; i < nLimited; i++) {
- for (let j = 0; j < nLimited; j++) {
- const tmp = arr[i].add(arr[j]);
- }
+ });
+ bench('add 100', () => {
+ const nLimited = 100;
+ const arr = setupVectors();
+
+ for (let i = 0; i < nLimited; i++) {
+ for (let j = 0; j < nLimited; j++) {
+ const tmp = arr[i].add(arr[j]);
}
- },
- );
-
+ }
+ });
});
diff --git a/test/js/chai_helpers.js b/test/js/chai_helpers.js
index 7018aa9c26..c2874c4eb8 100644
--- a/test/js/chai_helpers.js
+++ b/test/js/chai_helpers.js
@@ -9,14 +9,14 @@ assert.arrayApproximately = function (arr1, arr2, delta, desc) {
for (var i = 0; i < arr1.length; i++) {
assert.approximately(arr1[i], arr2[i], delta, desc);
}
-}
+};
assert.deepCloseTo = function (actual, expected, digits = 4) {
expect(actual.length).toBe(expected.length);
for (let i = 0; i < actual.length; i++) {
expect(actual[i]).withContext(`[${i}]`).toBeCloseTo(expected[i], digits);
}
-}
+};
// a custom assertion for validation errors that correctly handles
// minified p5 libraries.
@@ -26,4 +26,4 @@ assert.validationError = function (fn) {
} else {
assert.doesNotThrow(fn, Error, 'got unwanted exception');
}
-};
\ No newline at end of file
+};
diff --git a/test/js/mocks.js b/test/js/mocks.js
index 5de4e4a1ec..2a9150961d 100644
--- a/test/js/mocks.js
+++ b/test/js/mocks.js
@@ -8,10 +8,10 @@ const httpMocks = [
http.get('404file', () => {
return new HttpResponse('Not Found', {
status: 404,
- statusText: 'Not Found',
+ statusText: 'Not Found'
});
}),
- http.all('*', ({request}) => {
+ http.all('*', ({ request }) => {
return passthrough();
})
];
@@ -32,7 +32,7 @@ Object.assign(mockP5, {
const mockCanvas = document.createElement('canvas');
mockCanvas.id = 'myCanvasID';
-document.getElementsByTagName("body")[0].appendChild(mockCanvas);
+document.getElementsByTagName('body')[0].appendChild(mockCanvas);
export const mockP5Prototype = {
saveCanvas: vi.fn(),
diff --git a/test/js/modernizr.js b/test/js/modernizr.js
index f86cdd8244..15479c99eb 100644
--- a/test/js/modernizr.js
+++ b/test/js/modernizr.js
@@ -1,5 +1,2841 @@
/*! modernizr 3.5.0 (Custom Build) | MIT *
* https://modernizr.com/download/?-adownload-ambientlight-animation-apng-appearance-applicationcache-atobbtoa-audio-audioloop-audiopreload-backdropfilter-backgroundblendmode-backgroundcliptext-backgroundsize-batteryapi-bdi-beacon-bgpositionshorthand-bgpositionxy-bgrepeatspace_bgrepeatround-bgsizecover-blobconstructor-bloburls-blobworkers-borderimage-borderradius-boxshadow-boxsizing-canvas-canvasblending-canvastext-canvaswinding-capture-checked-classlist-contains-contenteditable-contextmenu-cookies-cors-cryptography-cssall-cssanimations-csscalc-csschunit-csscolumns-cssescape-cssexunit-cssfilters-cssgradients-csshyphens_softhyphens_softhyphensfind-cssinvalid-cssmask-csspointerevents-csspositionsticky-csspseudoanimations-csspseudotransitions-cssreflections-cssremunit-cssresize-cssscrollbar-csstransforms-csstransforms3d-csstransitions-cssvalid-cssvhunit-cssvmaxunit-cssvminunit-cssvwunit-cubicbezierrange-customevent-customprotocolhandler-dart-datalistelem-dataset-datauri-dataview-dataworkers-details-devicemotion_deviceorientation-directory-displaytable-documentfragment-ellipsis-emoji-es5-es5array-es5date-es5function-es5object-es5string-es5syntax-es5undefined-es6array-es6math-es6number-es6object-es6string-eventlistener-eventsource-exiforientation-fetch-fileinput-filereader-filesystem-flash-flexbox-flexboxlegacy-flexboxtweener-flexwrap-fontface-formattribute-formvalidation-framed-fullscreen-gamepads-generatedcontent-generators-geolocation-getrandomvalues-getusermedia-hashchange-hidden-hiddenscroll-history-hsla-htmlimports-ie8compat-indexeddb-indexeddbblob-inlinesvg-input-inputformaction-inputformenctype-inputformmethod-inputformtarget-inputtypes-intl-jpeg2000-jpegxr-json-lastchild-localizednumber-localstorage-lowbandwidth-lowbattery-matchmedia-mathml-mediaqueries-microdata-multiplebgs-mutationobserver-notification-nthchild-objectfit-olreversed-oninput-opacity-outputelem-overflowscrolling-pagevisibility-peerconnection-performance-picture-placeholder-pointerevents-pointerlock-postmessage-preserve3d-progressbar_meter-promises-proximity-queryselector-quotamanagement-regions-requestanimationframe-requestautocomplete-rgba-ruby-sandbox-scriptasync-scriptdefer-seamless-search-serviceworker-sessionstorage-shapes-sharedworkers-siblinggeneral-sizes-smil-speechrecognition-speechsynthesis-srcdoc-srcset-strictmode-stylescoped-subpixelfont-supports-svg-svgasimg-svgclippaths-svgfilters-svgforeignobject-target-template-templatestrings-textalignlast-textareamaxlength-textshadow-texttrackapi_track-time-todataurljpeg_todataurlpng_todataurlwebp-touchevents-transferables-typedarrays-unicode-unicoderange-unknownelements-urlparser-userdata-userselect-vibrate-video-videoautoplay-videoloop-videopreload-vml-webaudio-webgl-webglextensions-webintents-webp-webpalpha-webpanimation-websockets-websocketsbinary-websqldatabase-webworkers-willchange-wrapflow-xdomainrequest-xhr2-xhrresponsetype-xhrresponsetypearraybuffer-xhrresponsetypeblob-xhrresponsetypedocument-xhrresponsetypejson-xhrresponsetypetext-addtest-atrule-domprefixes-hasevent-mq-prefixed-prefixedcss-prefixedcssvalue-prefixes-setclasses-shiv-testallprops-testprop-teststyles !*/
-!function(window,document,undefined){function is(A,e){return typeof A===e}function testRunner(){var A,e,t,r,n,o,i;for(var d in tests)if(tests.hasOwnProperty(d)){if(A=[],e=tests[d],e.name&&(A.push(e.name.toLowerCase()),e.options&&e.options.aliases&&e.options.aliases.length))for(t=0;td;d++)if(s=A[d],l=mStyle.style[s],contains(s,"-")&&(s=cssToDOM(s)),mStyle.style[s]!==undefined){if(r||is(t,"undefined"))return n(),"pfx"==e?s:!0;try{mStyle.style[s]=t}catch(u){}if(mStyle.style[s]!=l)return n(),"pfx"==e?s:!0}return n(),!1}function fnBind(A,e){return function(){return A.apply(e,arguments)}}function testDOMProps(A,e,t){var r;for(var n in A)if(A[n]in e)return t===!1?A[n]:(r=e[A[n]],is(r,"function")?fnBind(r,t||e):r);return!1}function testPropsAll(A,e,t,r,n){var o=A.charAt(0).toUpperCase()+A.slice(1),i=(A+" "+cssomPrefixes.join(o+" ")+o).split(" ");return is(e,"string")||is(e,"undefined")?testProps(i,e,r,n):(i=(A+" "+domPrefixes.join(o+" ")+o).split(" "),testDOMProps(i,e,t))}function detectDeleteDatabase(A,e){var t=A.deleteDatabase(e);t.onsuccess=function(){addTest("indexeddb.deletedatabase",!0)},t.onerror=function(){addTest("indexeddb.deletedatabase",!1)}}function testAllProps(A,e,t){return testPropsAll(A,undefined,undefined,e,t)}var classes=[],tests=[],ModernizrProto={_version:"3.5.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(A,e){var t=this;setTimeout(function(){e(t[A])},0)},addTest:function(A,e,t){tests.push({name:A,fn:e,options:t})},addAsyncTest:function(A){tests.push({name:null,fn:A})}},Modernizr=function(){};Modernizr.prototype=ModernizrProto,Modernizr=new Modernizr,Modernizr.addTest("applicationcache","applicationCache"in window),Modernizr.addTest("blobconstructor",function(){try{return!!new Blob}catch(A){return!1}},{aliases:["blob-constructor"]}),Modernizr.addTest("cookies",function(){try{document.cookie="cookietest=1";var A=-1!=document.cookie.indexOf("cookietest=");return document.cookie="cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT",A}catch(e){return!1}}),Modernizr.addTest("cors","XMLHttpRequest"in window&&"withCredentials"in new XMLHttpRequest),Modernizr.addTest("customprotocolhandler",function(){if(!navigator.registerProtocolHandler)return!1;try{navigator.registerProtocolHandler("thisShouldFail")}catch(A){return A instanceof TypeError}return!1}),Modernizr.addTest("customevent","CustomEvent"in window&&"function"==typeof window.CustomEvent),Modernizr.addTest("dataview","undefined"!=typeof DataView&&"getFloat64"in DataView.prototype),Modernizr.addTest("eventlistener","addEventListener"in window),Modernizr.addTest("geolocation","geolocation"in navigator),Modernizr.addTest("history",function(){var A=navigator.userAgent;return-1===A.indexOf("Android 2.")&&-1===A.indexOf("Android 4.0")||-1===A.indexOf("Mobile Safari")||-1!==A.indexOf("Chrome")||-1!==A.indexOf("Windows Phone")||"file:"===location.protocol?window.history&&"pushState"in window.history:!1}),Modernizr.addTest("ie8compat",!window.addEventListener&&!!document.documentMode&&7===document.documentMode),Modernizr.addTest("json","JSON"in window&&"parse"in JSON&&"stringify"in JSON),Modernizr.addTest("notification",function(){if(!window.Notification||!window.Notification.requestPermission)return!1;if("granted"===window.Notification.permission)return!0;try{new window.Notification("")}catch(A){if("TypeError"===A.name)return!1}return!0}),Modernizr.addTest("postmessage","postMessage"in window),Modernizr.addTest("queryselector","querySelector"in document&&"querySelectorAll"in document),Modernizr.addTest("serviceworker","serviceWorker"in navigator),Modernizr.addTest("svg",!!document.createElementNS&&!!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect),Modernizr.addTest("templatestrings",function(){var supports;try{eval("``"),supports=!0}catch(e){}return!!supports}),Modernizr.addTest("typedarrays","ArrayBuffer"in window);var supports=!1;try{supports="WebSocket"in window&&2===window.WebSocket.CLOSING}catch(e){}Modernizr.addTest("websockets",supports),Modernizr.addTest("xdomainrequest","XDomainRequest"in window),Modernizr.addTest("webaudio",function(){var A="webkitAudioContext"in window,e="AudioContext"in window;return Modernizr._config.usePrefixes?A||e:e});var CSS=window.CSS;Modernizr.addTest("cssescape",CSS?"function"==typeof CSS.escape:!1);var newSyntax="CSS"in window&&"supports"in window.CSS,oldSyntax="supportsCSS"in window;Modernizr.addTest("supports",newSyntax||oldSyntax),Modernizr.addTest("target",function(){var A=window.document;if(!("querySelectorAll"in A))return!1;try{return A.querySelectorAll(":target"),!0}catch(e){return!1}}),Modernizr.addTest("microdata","getItems"in document),Modernizr.addTest("mutationobserver",!!window.MutationObserver||!!window.WebKitMutationObserver),Modernizr.addTest("picture","HTMLPictureElement"in window),Modernizr.addTest("es5array",function(){return!!(Array.prototype&&Array.prototype.every&&Array.prototype.filter&&Array.prototype.forEach&&Array.prototype.indexOf&&Array.prototype.lastIndexOf&&Array.prototype.map&&Array.prototype.some&&Array.prototype.reduce&&Array.prototype.reduceRight&&Array.isArray)}),Modernizr.addTest("es5date",function(){var A="2013-04-12T06:06:37.307Z",e=!1;try{e=!!Date.parse(A)}catch(t){}return!!(Date.now&&Date.prototype&&Date.prototype.toISOString&&Date.prototype.toJSON&&e)}),Modernizr.addTest("es5function",function(){return!(!Function.prototype||!Function.prototype.bind)}),Modernizr.addTest("es5object",function(){return!!(Object.keys&&Object.create&&Object.getPrototypeOf&&Object.getOwnPropertyNames&&Object.isSealed&&Object.isFrozen&&Object.isExtensible&&Object.getOwnPropertyDescriptor&&Object.defineProperty&&Object.defineProperties&&Object.seal&&Object.freeze&&Object.preventExtensions)}),Modernizr.addTest("strictmode",function(){"use strict";return!this}()),Modernizr.addTest("es5string",function(){return!(!String.prototype||!String.prototype.trim)}),Modernizr.addTest("es5syntax",function(){var value,obj,stringAccess,getter,setter,reservedWords,zeroWidthChars;try{return stringAccess=eval('"foobar"[3] === "b"'),getter=eval("({ get x(){ return 1 } }).x === 1"),eval("({ set x(v){ value = v; } }).x = 1"),setter=1===value,eval("obj = ({ if: 1 })"),reservedWords=1===obj["if"],zeroWidthChars=eval("_ = true"),stringAccess&&getter&&setter&&reservedWords&&zeroWidthChars}catch(ignore){return!1}}),Modernizr.addTest("es5undefined",function(){var A,e;try{e=window.undefined,window.undefined=12345,A="undefined"==typeof window.undefined,window.undefined=e}catch(t){return!1}return A}),Modernizr.addTest("es5",function(){return!!(Modernizr.es5array&&Modernizr.es5date&&Modernizr.es5function&&Modernizr.es5object&&Modernizr.strictmode&&Modernizr.es5string&&Modernizr.json&&Modernizr.es5syntax&&Modernizr.es5undefined)}),Modernizr.addTest("es6array",!!(Array.prototype&&Array.prototype.copyWithin&&Array.prototype.fill&&Array.prototype.find&&Array.prototype.findIndex&&Array.prototype.keys&&Array.prototype.entries&&Array.prototype.values&&Array.from&&Array.of)),Modernizr.addTest("generators",function(){try{new Function("function* test() {}")()}catch(A){return!1}return!0}),Modernizr.addTest("es6math",!!(Math&&Math.clz32&&Math.cbrt&&Math.imul&&Math.sign&&Math.log10&&Math.log2&&Math.log1p&&Math.expm1&&Math.cosh&&Math.sinh&&Math.tanh&&Math.acosh&&Math.asinh&&Math.atanh&&Math.hypot&&Math.trunc&&Math.fround)),Modernizr.addTest("es6number",!!(Number.isFinite&&Number.isInteger&&Number.isSafeInteger&&Number.isNaN&&Number.parseInt&&Number.parseFloat&&Number.isInteger(Number.MAX_SAFE_INTEGER)&&Number.isInteger(Number.MIN_SAFE_INTEGER)&&Number.isFinite(Number.EPSILON))),Modernizr.addTest("es6string",!!(String.fromCodePoint&&String.raw&&String.prototype.codePointAt&&String.prototype.repeat&&String.prototype.startsWith&&String.prototype.endsWith&&String.prototype.includes)),Modernizr.addTest("promises",function(){return"Promise"in window&&"resolve"in window.Promise&&"reject"in window.Promise&&"all"in window.Promise&&"race"in window.Promise&&function(){var A;return new window.Promise(function(e){A=e}),"function"==typeof A}()}),Modernizr.addTest("devicemotion","DeviceMotionEvent"in window),Modernizr.addTest("deviceorientation","DeviceOrientationEvent"in window),Modernizr.addTest("es6object",!!(Object.assign&&Object.is&&Object.setPrototypeOf)),Modernizr.addTest("filereader",!!(window.File&&window.FileList&&window.FileReader)),Modernizr.addTest("beacon","sendBeacon"in navigator),Modernizr.addTest("lowbandwidth",function(){var A=navigator.connection||{type:0};return 3==A.type||4==A.type||/^[23]g$/.test(A.type)}),Modernizr.addTest("eventsource","EventSource"in window),Modernizr.addTest("fetch","fetch"in window),Modernizr.addTest("xhrresponsetype",function(){if("undefined"==typeof XMLHttpRequest)return!1;var A=new XMLHttpRequest;return A.open("get","/",!0),"response"in A}()),Modernizr.addTest("xhr2","XMLHttpRequest"in window&&"withCredentials"in new XMLHttpRequest),Modernizr.addTest("speechsynthesis","SpeechSynthesisUtterance"in window),Modernizr.addTest("localstorage",function(){var A="modernizr";try{return localStorage.setItem(A,A),localStorage.removeItem(A),!0}catch(e){return!1}}),Modernizr.addTest("sessionstorage",function(){var A="modernizr";try{return sessionStorage.setItem(A,A),sessionStorage.removeItem(A),!0}catch(e){return!1}}),Modernizr.addTest("websqldatabase","openDatabase"in window),Modernizr.addTest("svgfilters",function(){var A=!1;try{A="SVGFEColorMatrixElement"in window&&2==SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE}catch(e){}return A}),Modernizr.addTest("urlparser",function(){var A;try{return A=new URL("http://modernizr.com/"),"http://modernizr.com/"===A.href}catch(e){return!1}}),Modernizr.addTest("websocketsbinary",function(){var A,e="https:"==location.protocol?"wss":"ws";if("WebSocket"in window){if(A="binaryType"in WebSocket.prototype)return A;try{return!!new WebSocket(e+"://.").binaryType}catch(t){}}return!1}),Modernizr.addTest("atobbtoa","atob"in window&&"btoa"in window,{aliases:["atob-btoa"]}),Modernizr.addTest("framed",window.location!=top.location),Modernizr.addTest("sharedworkers","SharedWorker"in window),Modernizr.addTest("webworkers","Worker"in window);var prefixes=ModernizrProto._config.usePrefixes?" -webkit- -moz- -o- -ms- ".split(" "):["",""];ModernizrProto._prefixes=prefixes,Modernizr.addTest("contains",is(String.prototype.contains,"function"));var docElement=document.documentElement;Modernizr.addTest("contextmenu","contextMenu"in docElement&&"HTMLMenuItemElement"in window),Modernizr.addTest("cssall","all"in docElement.style),Modernizr.addTest("willchange","willChange"in docElement.style),Modernizr.addTest("classlist","classList"in docElement),Modernizr.addTest("documentfragment",function(){return"createDocumentFragment"in document&&"appendChild"in docElement});var isSVG="svg"===docElement.nodeName.toLowerCase(),html5;isSVG||!function(A,e){function t(A,e){var t=A.createElement("p"),r=A.getElementsByTagName("head")[0]||A.documentElement;return t.innerHTML="x",r.insertBefore(t.lastChild,r.firstChild)}function r(){var A=h.elements;return"string"==typeof A?A.split(" "):A}function n(A,e){var t=h.elements;"string"!=typeof t&&(t=t.join(" ")),"string"!=typeof A&&(A=A.join(" ")),h.elements=t+" "+A,s(e)}function o(A){var e=E[A[f]];return e||(e={},g++,A[f]=g,E[g]=e),e}function i(A,t,r){if(t||(t=e),c)return t.createElement(A);r||(r=o(t));var n;return n=r.cache[A]?r.cache[A].cloneNode():m.test(A)?(r.cache[A]=r.createElem(A)).cloneNode():r.createElem(A),!n.canHaveChildren||w.test(A)||n.tagUrn?n:r.frag.appendChild(n)}function d(A,t){if(A||(A=e),c)return A.createDocumentFragment();t=t||o(A);for(var n=t.frag.cloneNode(),i=0,d=r(),a=d.length;a>i;i++)n.createElement(d[i]);return n}function a(A,e){e.cache||(e.cache={},e.createElem=A.createElement,e.createFrag=A.createDocumentFragment,e.frag=e.createFrag()),A.createElement=function(t){return h.shivMethods?i(t,A,e):e.createElem(t)},A.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-:]+/g,function(A){return e.createElem(A),e.frag.createElement(A),'c("'+A+'")'})+");return n}")(h,e.frag)}function s(A){A||(A=e);var r=o(A);return!h.shivCSS||l||r.hasCSS||(r.hasCSS=!!t(A,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),c||a(A,r),A}var l,c,u="3.7.3",p=A.html5||{},w=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,m=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,f="_html5shiv",g=0,E={};!function(){try{var A=e.createElement("a");A.innerHTML=" ",l="hidden"in A,c=1==A.childNodes.length||function(){e.createElement("a");var A=e.createDocumentFragment();return"undefined"==typeof A.cloneNode||"undefined"==typeof A.createDocumentFragment||"undefined"==typeof A.createElement}()}catch(t){l=!0,c=!0}}();var h={elements:p.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:u,shivCSS:p.shivCSS!==!1,supportsUnknownElements:c,shivMethods:p.shivMethods!==!1,type:"default",shivDocument:s,createElement:i,createDocumentFragment:d,addElements:n};A.html5=h,s(e),"object"==typeof module&&module.exports&&(module.exports=h)}("undefined"!=typeof window?window:this,document);var omPrefixes="Moz O ms Webkit",domPrefixes=ModernizrProto._config.usePrefixes?omPrefixes.toLowerCase().split(" "):[];ModernizrProto._domPrefixes=domPrefixes;var hasOwnProp;!function(){var A={}.hasOwnProperty;hasOwnProp=is(A,"undefined")||is(A.call,"undefined")?function(A,e){return e in A&&is(A.constructor.prototype[e],"undefined")}:function(e,t){return A.call(e,t)}}(),ModernizrProto._l={},ModernizrProto.on=function(A,e){this._l[A]||(this._l[A]=[]),this._l[A].push(e),Modernizr.hasOwnProperty(A)&&setTimeout(function(){Modernizr._trigger(A,Modernizr[A])},0)},ModernizrProto._trigger=function(A,e){if(this._l[A]){var t=this._l[A];setTimeout(function(){var A,r;for(A=0;An;n++){var o=prefixes[n],i=o.toUpperCase()+"_"+e;if(i in r)return"@-"+o.toLowerCase()+"-"+A}return!1};ModernizrProto.atRule=atRule;var hasEvent=function(){function A(A,t){var r;return A?(t&&"string"!=typeof t||(t=createElement(t||"div")),A="on"+A,r=A in t,!r&&e&&(t.setAttribute||(t=createElement("div")),t.setAttribute(A,""),r="function"==typeof t[A],t[A]!==undefined&&(t[A]=undefined),t.removeAttribute(A)),r):!1}var e=!("onblur"in document.documentElement);return A}();ModernizrProto.hasEvent=hasEvent,Modernizr.addTest("ambientlight",hasEvent("devicelight",window)),Modernizr.addTest("hashchange",function(){return hasEvent("hashchange",window)===!1?!1:document.documentMode===undefined||document.documentMode>7}),Modernizr.addTest("inputsearchevent",hasEvent("search")),Modernizr.addTest("pointerevents",function(){var A=!1,e=domPrefixes.length;for(A=Modernizr.hasEvent("pointerdown");e--&&!A;)hasEvent(domPrefixes[e]+"pointerdown")&&(A=!0);return A});var prefixedCSSValue=function(A,e){var t=!1,r=createElement("div"),n=r.style;if(A in n){var o=domPrefixes.length;for(n[A]=e,t=n[A];o--&&!t;)n[A]="-"+domPrefixes[o]+"-"+e,t=n[A]}return""===t&&(t=!1),t};ModernizrProto.prefixedCSSValue=prefixedCSSValue,Modernizr.addTest("audio",function(){var A=createElement("audio"),e=!1;try{e=!!A.canPlayType,e&&(e=new Boolean(e),e.ogg=A.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),e.mp3=A.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/,""),e.opus=A.canPlayType('audio/ogg; codecs="opus"')||A.canPlayType('audio/webm; codecs="opus"').replace(/^no$/,""),e.wav=A.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),e.m4a=(A.canPlayType("audio/x-m4a;")||A.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(t){}return e}),Modernizr.addTest("canvas",function(){var A=createElement("canvas");return!(!A.getContext||!A.getContext("2d"))}),Modernizr.addTest("canvastext",function(){return Modernizr.canvas===!1?!1:"function"==typeof createElement("canvas").getContext("2d").fillText}),Modernizr.addTest("contenteditable",function(){if("contentEditable"in docElement){var A=createElement("div");return A.contentEditable=!0,"true"===A.contentEditable}}),Modernizr.addTest("emoji",function(){if(!Modernizr.canvastext)return!1;var A=window.devicePixelRatio||1,e=12*A,t=createElement("canvas"),r=t.getContext("2d");return r.fillStyle="#f00",r.textBaseline="top",r.font="32px Arial",r.fillText("🐨",0,0),0!==r.getImageData(e,e,1,1).data[0]}),addTest("htmlimports","import"in createElement("link")),Modernizr.addTest("olreversed","reversed"in createElement("ol")),Modernizr.addTest("userdata",!!createElement("div").addBehavior),Modernizr.addTest("video",function(){var A=createElement("video"),e=!1;try{e=!!A.canPlayType,e&&(e=new Boolean(e),e.ogg=A.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),e.h264=A.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),e.webm=A.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""),e.vp9=A.canPlayType('video/webm; codecs="vp9"').replace(/^no$/,""),e.hls=A.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/,""))}catch(t){}return e}),Modernizr.addTest("vml",function(){var A,e=createElement("div"),t=!1;return isSVG||(e.innerHTML=' ',A=e.firstChild,"style"in A&&(A.style.behavior="url(#default#VML)"),t=A?"object"==typeof A.adj:!0),t}),Modernizr.addTest("webanimations","animate"in createElement("div")),Modernizr.addTest("webgl",function(){var A=createElement("canvas"),e="probablySupportsContext"in A?"probablySupportsContext":"supportsContext";return e in A?A[e]("webgl")||A[e]("experimental-webgl"):"WebGLRenderingContext"in window}),Modernizr.addTest("adownload",!window.externalHost&&"download"in createElement("a")),Modernizr.addTest("audioloop","loop"in createElement("audio")),Modernizr.addAsyncTest(function(){function A(t){clearTimeout(e);var n=t!==undefined&&"loadeddata"===t.type?!0:!1;r.removeEventListener("loadeddata",A,!1),addTest("audiopreload",n),r.parentNode.removeChild(r)}var e,t=300,r=createElement("audio"),n=r.style;if(!(Modernizr.audio&&"preload"in r))return void addTest("audiopreload",!1);n.position="absolute",n.height=0,n.width=0;try{if(Modernizr.audio.mp3)r.src="data:audio/mpeg;base64,//MUxAAB6AXgAAAAAPP+c6nf//yi/6f3//MUxAMAAAIAAAjEcH//0fTX6C9Lf//0//MUxA4BeAIAAAAAAKX2/6zv//+IlR4f//MUxBMCMAH8AAAAABYWalVMQU1FMy45//MUxBUB0AH0AAAAADkuM1VVVVVVVVVV//MUxBgBUATowAAAAFVVVVVVVVVVVVVV";else if(Modernizr.audio.m4a)r.src="data:audio/x-m4a;base64,AAAAGGZ0eXBNNEEgAAACAGlzb21pc28yAAAACGZyZWUAAAAfbWRhdN4EAABsaWJmYWFjIDEuMjgAAAFoAQBHAAACiG1vb3YAAABsbXZoZAAAAAB8JbCAfCWwgAAAA+gAAAAYAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAG0dHJhawAAAFx0a2hkAAAAD3wlsIB8JbCAAAAAAQAAAAAAAAAYAAAAAAAAAAAAAAAAAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAABUG1kaWEAAAAgbWRoZAAAAAB8JbCAfCWwgAAArEQAAAQAVcQAAAAAAC1oZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU291bmRIYW5kbGVyAAAAAPttaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAL9zdGJsAAAAW3N0c2QAAAAAAAAAAQAAAEttcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAACdlc2RzAAAAAAMZAAEABBFAFQAAAAABftAAAAAABQISCAYBAgAAABhzdHRzAAAAAAAAAAEAAAABAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAXAAAAAQAAABRzdGNvAAAAAAAAAAEAAAAoAAAAYHVkdGEAAABYbWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAraWxzdAAAACOpdG9vAAAAG2RhdGEAAAABAAAAAExhdmY1Mi42NC4y";else if(Modernizr.audio.ogg)r.src="data:audio/ogg;base64,T2dnUwACAAAAAAAAAAD/QwAAAAAAAM2LVKsBHgF2b3JiaXMAAAAAAUSsAAAAAAAAgLsAAAAAAAC4AU9nZ1MAAAAAAAAAAAAA/0MAAAEAAADmvOe6Dy3/////////////////MgN2b3JiaXMdAAAAWGlwaC5PcmcgbGliVm9yYmlzIEkgMjAwNzA2MjIAAAAAAQV2b3JiaXMfQkNWAQAAAQAYY1QpRplS0kqJGXOUMUaZYpJKiaWEFkJInXMUU6k515xrrLm1IIQQGlNQKQWZUo5SaRljkCkFmVIQS0kldBI6J51jEFtJwdaYa4tBthyEDZpSTCnElFKKQggZU4wpxZRSSkIHJXQOOuYcU45KKEG4nHOrtZaWY4updJJK5yRkTEJIKYWSSgelU05CSDWW1lIpHXNSUmpB6CCEEEK2IIQNgtCQVQAAAQDAQBAasgoAUAAAEIqhGIoChIasAgAyAAAEoCiO4iiOIzmSY0kWEBqyCgAAAgAQAADAcBRJkRTJsSRL0ixL00RRVX3VNlVV9nVd13Vd13UgNGQVAAABAEBIp5mlGiDCDGQYCA1ZBQAgAAAARijCEANCQ1YBAAABAABiKDmIJrTmfHOOg2Y5aCrF5nRwItXmSW4q5uacc845J5tzxjjnnHOKcmYxaCa05pxzEoNmKWgmtOacc57E5kFrqrTmnHPGOaeDcUYY55xzmrTmQWo21uaccxa0pjlqLsXmnHMi5eZJbS7V5pxzzjnnnHPOOeecc6oXp3NwTjjnnHOi9uZabkIX55xzPhmne3NCOOecc84555xzzjnnnHOC0JBVAAAQAABBGDaGcacgSJ+jgRhFiGnIpAfdo8MkaAxyCqlHo6ORUuoglFTGSSmdIDRkFQAACAAAIYQUUkghhRRSSCGFFFKIIYYYYsgpp5yCCiqppKKKMsoss8wyyyyzzDLrsLPOOuwwxBBDDK20EktNtdVYY62555xrDtJaaa211koppZRSSikIDVkFAIAAABAIGWSQQUYhhRRSiCGmnHLKKaigAkJDVgEAgAAAAgAAADzJc0RHdERHdERHdERHdETHczxHlERJlERJtEzL1ExPFVXVlV1b1mXd9m1hF3bd93Xf93Xj14VhWZZlWZZlWZZlWZZlWZZlWYLQkFUAAAgAAIAQQgghhRRSSCGlGGPMMeegk1BCIDRkFQAACAAgAAAAwFEcxXEkR3IkyZIsSZM0S7M8zdM8TfREURRN01RFV3RF3bRF2ZRN13RN2XRVWbVdWbZt2dZtX5Zt3/d93/d93/d93/d93/d1HQgNWQUASAAA6EiOpEiKpEiO4ziSJAGhIasAABkAAAEAKIqjOI7jSJIkSZakSZ7lWaJmaqZneqqoAqEhqwAAQAAAAQAAAAAAKJriKabiKaLiOaIjSqJlWqKmaq4om7Lruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7rui4QGrIKAJAAANCRHMmRHEmRFEmRHMkBQkNWAQAyAAACAHAMx5AUybEsS9M8zdM8TfRET/RMTxVd0QVCQ1YBAIAAAAIAAAAAADAkw1IsR3M0SZRUS7VUTbVUSxVVT1VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTVN0zRNIDRkJQAABADAYo3B5SAhJSXl3hDCEJOeMSYhtV4hBJGS3jEGFYOeMqIMct5C4xCDHggNWREARAEAAMYgxxBzyDlHqZMSOeeodJQa5xyljlJnKcWYYs0oldhSrI1zjlJHraOUYiwtdpRSjanGAgAAAhwAAAIshEJDVgQAUQAAhDFIKaQUYow5p5xDjCnnmHOGMeYcc44556B0UirnnHROSsQYc445p5xzUjonlXNOSiehAACAAAcAgAALodCQFQFAnACAQZI8T/I0UZQ0TxRFU3RdUTRd1/I81fRMU1U90VRVU1Vt2VRVWZY8zzQ901RVzzRV1VRVWTZVVZZFVdVt03V123RV3ZZt2/ddWxZ2UVVt3VRd2zdV1/Zd2fZ9WdZ1Y/I8VfVM03U903Rl1XVtW3VdXfdMU5ZN15Vl03Vt25VlXXdl2fc103Rd01Vl2XRd2XZlV7ddWfZ903WF35VlX1dlWRh2XfeFW9eV5XRd3VdlVzdWWfZ9W9eF4dZ1YZk8T1U903RdzzRdV3VdX1dd19Y105Rl03Vt2VRdWXZl2fddV9Z1zzRl2XRd2zZdV5ZdWfZ9V5Z13XRdX1dlWfhVV/Z1WdeV4dZt4Tdd1/dVWfaFV5Z14dZ1Ybl1XRg+VfV9U3aF4XRl39eF31luXTiW0XV9YZVt4VhlWTl+4ViW3feVZXRdX1ht2RhWWRaGX/id5fZ943h1XRlu3efMuu8Mx++k+8rT1W1jmX3dWWZfd47hGDq/8OOpqq+brisMpywLv+3rxrP7vrKMruv7qiwLvyrbwrHrvvP8vrAso+z6wmrLwrDatjHcvm4sv3Acy2vryjHrvlG2dXxfeArD83R1XXlmXcf2dXTjRzh+ygAAgAEHAIAAE8pAoSErAoA4AQCPJImiZFmiKFmWKIqm6LqiaLqupGmmqWmeaVqaZ5qmaaqyKZquLGmaaVqeZpqap5mmaJqua5qmrIqmKcumasqyaZqy7LqybbuubNuiacqyaZqybJqmLLuyq9uu7Oq6pFmmqXmeaWqeZ5qmasqyaZquq3meanqeaKqeKKqqaqqqraqqLFueZ5qa6KmmJ4qqaqqmrZqqKsumqtqyaaq2bKqqbbuq7Pqybeu6aaqybaqmLZuqatuu7OqyLNu6L2maaWqeZ5qa55mmaZqybJqqK1uep5qeKKqq5ommaqqqLJumqsqW55mqJ4qq6omea5qqKsumatqqaZq2bKqqLZumKsuubfu+68qybqqqbJuqauumasqybMu+78qq7oqmKcumqtqyaaqyLduy78uyrPuiacqyaaqybaqqLsuybRuzbPu6aJqybaqmLZuqKtuyLfu6LNu678qub6uqrOuyLfu67vqucOu6MLyybPuqrPq6K9u6b+sy2/Z9RNOUZVM1bdtUVVl2Zdn2Zdv2fdE0bVtVVVs2TdW2ZVn2fVm2bWE0Tdk2VVXWTdW0bVmWbWG2ZeF2Zdm3ZVv2ddeVdV/XfePXZd3murLty7Kt+6qr+rbu+8Jw667wCgAAGHAAAAgwoQwUGrISAIgCAACMYYwxCI1SzjkHoVHKOecgZM5BCCGVzDkIIZSSOQehlJQy5yCUklIIoZSUWgshlJRSawUAABQ4AAAE2KApsThAoSErAYBUAACD41iW55miatqyY0meJ4qqqaq27UiW54miaaqqbVueJ4qmqaqu6+ua54miaaqq6+q6aJqmqaqu67q6Lpqiqaqq67qyrpumqqquK7uy7Oumqqqq68quLPvCqrquK8uybevCsKqu68qybNu2b9y6ruu+7/vCka3rui78wjEMRwEA4AkOAEAFNqyOcFI0FlhoyEoAIAMAgDAGIYMQQgYhhJBSSiGllBIAADDgAAAQYEIZKDRkRQAQJwAAGEMppJRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkgppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkqppJRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSCgCQinAAkHowoQwUGrISAEgFAACMUUopxpyDEDHmGGPQSSgpYsw5xhyUklLlHIQQUmktt8o5CCGk1FJtmXNSWosx5hgz56SkFFvNOYdSUoux5ppr7qS0VmuuNedaWqs115xzzbm0FmuuOdecc8sx15xzzjnnGHPOOeecc84FAOA0OACAHtiwOsJJ0VhgoSErAYBUAAACGaUYc8456BBSjDnnHIQQIoUYc845CCFUjDnnHHQQQqgYc8w5CCGEkDnnHIQQQgghcw466CCEEEIHHYQQQgihlM5BCCGEEEooIYQQQgghhBA6CCGEEEIIIYQQQgghhFJKCCGEEEIJoZRQAABggQMAQIANqyOcFI0FFhqyEgAAAgCAHJagUs6EQY5Bjw1BylEzDUJMOdGZYk5qMxVTkDkQnXQSGWpB2V4yCwAAgCAAIMAEEBggKPhCCIgxAABBiMwQCYVVsMCgDBoc5gHAA0SERACQmKBIu7iALgNc0MVdB0IIQhCCWBxAAQk4OOGGJ97whBucoFNU6iAAAAAAAAwA4AEA4KAAIiKaq7C4wMjQ2ODo8AgAAAAAABYA+AAAOD6AiIjmKiwuMDI0Njg6PAIAAAAAAAAAAICAgAAAAAAAQAAAAICAT2dnUwAE7AwAAAAAAAD/QwAAAgAAADuydfsFAQEBAQEACg4ODg==";else{
-if(!Modernizr.audio.wav)return void addTest("audiopreload",!1);r.src="data:audio/wav;base64,UklGRvwZAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YdgZAAAAAAEA/v8CAP//AAABAP////8DAPz/BAD9/wEAAAAAAAAAAAABAP7/AgD//wAAAQD//wAAAQD//wAAAQD+/wIA//8AAAAAAAD//wIA/v8BAAAA//8BAAAA//8BAP//AQAAAP//AQD//wEAAAD//wEA//8BAP//AQD//wEA//8BAP//AQD+/wMA/f8DAP3/AgD+/wIA/////wMA/f8CAP7/AgD+/wMA/f8CAP7/AgD//wAAAAAAAAAAAQD+/wIA/v8CAP7/AwD9/wIA/v8BAAEA/v8CAP7/AQAAAAAAAAD//wEAAAD//wIA/f8DAP7/AQD//wEAAAD//wEA//8CAP7/AQD//wIA/v8CAP7/AQAAAAAAAAD//wEAAAAAAAAA//8BAP//AgD9/wQA+/8FAPz/AgAAAP//AgD+/wEAAAD//wIA/v8CAP3/BAD8/wQA/P8DAP7/AwD8/wQA/P8DAP7/AQAAAAAA//8BAP//AgD+/wEAAAD//wIA/v8BAP//AQD//wEAAAD//wEA//8BAAAAAAAAAP//AgD+/wEAAAAAAAAAAAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AgD+/wIA/v8BAP//AQABAP7/AQD//wIA/v8CAP3/AwD/////AgD9/wMA/v8BAP//AQAAAP//AQD//wEA//8BAP//AAABAP//AAABAP//AQD//wAAAAACAP3/AwD9/wIA//8BAP//AQD//wEA//8BAP//AgD9/wMA/v8AAAIA/f8CAAAA/v8EAPv/BAD9/wIAAAD+/wQA+v8HAPr/BAD+/wEAAAD//wIA/f8EAPz/BAD7/wUA/P8EAPz/AwD+/wEAAAD//wEAAAAAAP//AgD8/wUA+/8FAPz/AwD9/wIA//8AAAEA/v8CAP//AQD//wAAAAABAP//AgD9/wMA/f8EAPz/AwD+/wAAAwD7/wUA/P8DAP7/AQAAAP//AgD+/wEAAQD+/wIA/v8BAAEA/v8CAP7/AQAAAP//AgD9/wMA/f8DAP7/AgD+/wEAAAAAAAEA//8AAAEA/v8DAP3/AgD//wEA//8BAP7/AwD9/wMA/v8BAP//AQAAAP//AgD9/wMA/v8BAP//AQAAAP//AgD+/wEAAQD+/wIA/////wIA//8AAAEA/f8DAP//AAABAP////8DAP3/AwD+/wEA//8BAP//AQAAAAAA//8BAP//AQD//wEA//8BAP//AAAAAAEA//8BAP7/AgD//wEA//8AAAAAAAAAAAAAAAD//wIA/v8BAAAA//8BAAEA/v8BAAAA//8DAPz/AwD+/wIA/v8CAP3/AwD+/wEAAAD//wEA//8BAAAA//8BAAAA/v8EAPv/BAD+/wAAAAABAP7/AgD//wAAAAABAP7/AgD//wAAAAAAAAAAAAABAP3/BAD8/wQA/f8BAAAAAAABAP7/AgD+/wIA/v8CAP7/AgD+/wIA/v8BAAAAAAD//wIA/f8DAP7/AAABAP//AAACAPz/BAD9/wIA//8AAP//AwD9/wMA/P8EAP3/AwD9/wIA//8BAP//AQD+/wMA/f8DAP7/AAABAP//AQAAAP//AQD//wIA/f8DAP7/AQAAAP//AQAAAAAA//8CAP7/AQABAP7/AgD+/wEAAQD+/wIA/v8CAP////8CAP7/AgD//wAAAAABAP7/AwD9/wIAAAD+/wMA/f8CAP//AQD+/wMA/f8CAP//AAACAPz/BQD6/wUA/v///wIA/v8CAP3/BAD7/wYA+v8FAPz/AwD/////AgD+/wEAAAD//wEAAAD//wIA/f8DAP7/AQAAAP//AgD//wAA//8BAAAAAAAAAP//AQD//wEA//8AAAIA/f8DAP3/AgAAAP//AQD//wEA//8AAAEA//8BAP////8CAP//AAABAP3/BAD9/wIA/v8BAAEA//8BAP7/AgD//wEA//8AAAEA//8BAP//AAAAAAEA//8BAP7/AgD//wEA//8AAAAAAQD+/wIA/v8BAAAAAAD//wIA/v8BAAAAAAAAAAAAAQD+/wMA/f8CAP//AQD//wIA/f8DAP7/AQD//wEA//8CAP7/AAABAP7/AwD9/wMA/v8AAAEA//8BAAAAAAD//wIA/v8BAAAA//8CAP7/AgD+/wEA//8CAP7/AgD//wAAAAAAAAAAAQD//wEA/v8DAPz/BQD8/wIA//8AAAEAAAD//wEA//8BAP//AQAAAAAA//8BAP//AgD+/wEAAAAAAP//AQD+/wMA/////wEA/v8CAP//AQD//wEA//8AAAEA//8BAAAA/v8EAPz/AwD+/wEAAAAAAAAA//8CAP7/AQD//wEA//8BAP//AAABAP7/AwD9/wIA//8BAP//AQD//wEA//8AAAEA/v8EAPv/BAD9/wIA//8BAP7/AwD9/wIA//8AAAEA//8BAP//AQD//wAAAQD//wEAAAD+/wMA/v8AAAIA/f8DAP7/AQD//wAAAQD+/wMA/f8CAP//AAABAP7/AgD+/wMA/f8CAP7/AQABAP7/AgD+/wIA/v8CAP7/AwD8/wMA//8AAAEA//8AAAAAAAABAP//AQD//wAAAQD//wIA/f8DAP3/AwD+/wAAAgD9/wIA//8AAAEAAAD+/wMA/P8FAPv/BAD9/wIA//8AAP//AgD+/wIA/v8BAAAAAAD//wEAAAAAAP//AQD//wEA//8BAP//AAABAP7/AwD9/wIA//8BAP//AAABAP//AQD//wAAAQD//wEA//8BAP//AAABAAAA//8BAP7/AwD9/wMA/f8DAP3/AgD//wEA//8BAP7/AgD//wAAAgD8/wQA/f8CAP//AQD+/wMA/f8CAP7/AgD//wAAAAAAAAAAAAABAP7/AwD9/wIA/v8DAP3/AwD9/wIA/v8DAPz/BQD7/wQA/f8CAP7/AwD9/wMA/f8CAP//AQAAAP7/AwD+/wEA//8AAAEAAAAAAP//AAABAP//AQAAAP7/AwD9/wMA/f8CAP//AQD//wEA//8AAAIA/f8CAAAA//8BAAAA//8BAAAA/v8EAPv/BAD9/wIA//8AAAEA/v8CAP//AAABAP//AAABAP//AAABAP7/AwD8/wQA/f8CAAAA/v8DAP3/AwD9/wMA/v8BAAAA//8BAAAA//8CAP7/AQAAAAAAAAAAAAAA//8CAP7/AgD+/wIA/v8CAP7/AgD//wAAAQD//wAAAQD//wAAAQD//wAAAQD+/wIA//8AAAAAAQD+/wMA/f8CAP//AQD//wEA//8AAAEA/v8DAP3/AgD//wAAAAABAP7/AwD9/wIA//8AAAEA/v8DAP3/AgD//wAAAAABAP7/AwD8/wMA/v8CAP//AAD//wIA/v8CAP7/AQABAP7/AQAAAP//AgD/////AQD//wEAAAD//wEA/v8EAPv/BAD9/wMA/v8BAAAA//8BAAEA/P8GAPr/BQD8/wMA/v8BAAAA//8CAP7/AQABAP3/BAD7/wYA+/8EAPz/AwD//wEA//8BAP7/BAD8/wMA/v8AAAIA/v8BAAAA//8BAAAA//8BAAAA//8CAP3/AwD+/wAAAgD8/wUA/P8DAP7/AAABAAAAAAD//wEAAAD//wIA/f8DAP7/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA/f8EAPz/AwD/////AgD+/wIA/f8DAP7/AgD+/wEA//8CAP7/AQD//wEAAAAAAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAP//AQAAAP//AAACAP3/BAD7/wQA/v8BAAAA//8BAP//AQAAAP//AQAAAP7/BAD7/wUA+/8EAP3/AgD//wAAAQD+/wIA//8AAAEA/v8CAP//AQD+/wEAAAAAAAAAAAD//wEA//8CAP3/AwD9/wIA//8AAAAAAAAAAAAA//8BAP//AgD+/wEA//8CAP7/AQAAAP//AgD/////AgD/////AgD+/wIA//8AAP//AQABAP7/AgD9/wMA/v8CAP////8BAAAAAAAAAAAA//8CAP////8DAPz/AwD+/wEAAAAAAP//AQD//wEAAAD//wEAAAD+/wQA+/8FAPz/AgAAAP//AgD9/wMA/v8BAAAAAAD//wEAAAD//wIA/v8BAAAAAAD//wIA/v8BAAAA//8BAAAA//8CAP7/AQD//wEA//8BAAAA//8BAP//AAABAP//AQAAAP7/AgD//wEA//8AAAAAAQD+/wMA/P8EAP7///8DAPz/BQD8/wEAAQD+/wMA/v8AAAEA//8BAP//AQD//wEA/v8CAP//AQD//wAAAAABAAAA//8BAP//AQAAAAAA//8BAP//AgD+/wAAAQD//wIA/f8CAP//AQAAAP7/AwD9/wMA/v8BAP//AAABAP//AgD9/wIA//8BAAAA//8BAAAA//8CAP3/AwD+/wEAAAD+/wQA/P8DAP7/AAACAP7/AQAAAP//AQAAAP//AQAAAP//AgD9/wIAAAD//wIA/f8DAP7/AQD//wEA//8CAP7/AQD//wAAAQD//wEA//8AAAAAAQD//wEAAAD9/wUA+/8FAPz/AgD//wAAAQD//wAAAQD+/wMA/f8BAAEA/v8CAP7/AgD+/wIA/v8BAAAAAAAAAAAAAAD//wIA/v8CAP////8CAP7/AgD+/wIA/v8CAP7/AQAAAP//AQAAAP//AQD//wAAAQD//wAAAQD+/wMA/f8CAAAA/v8DAP3/AgAAAP//AQAAAP7/AwD9/wMA/v8BAP//AQD//wEAAAD+/wMA/f8CAAAA/v8CAP//AAAAAAEA//8AAAEA/v8DAP3/AwD9/wIA//8BAP//AgD8/wQA/v8BAAAA/v8CAP//AQD//wAAAAAAAAEA/f8EAPz/BAD9/wIA//8AAAAAAAABAP//AAAAAAAAAAABAP3/BAD9/wIA/v8BAAEA//8AAAAA//8CAP7/AgD9/wQA+/8FAPv/BQD8/wMA/f8DAP3/AwD+/wAAAgD9/wMA/f8CAAAA/v8EAPv/BQD7/wUA/P8DAP///v8DAP3/BAD8/wMA/f8DAP7/AQD//wEAAAD//wEA/v8CAAAA/v8CAP7/AgD//wAAAAAAAAAAAQD+/wIA//8AAAEA/v8DAPz/BAD9/wIA//8AAP//AgD//wEA/v8BAAAAAQD//wAAAAAAAAEA//8AAAEA//8BAP//AAABAP//AQD+/wIA/v8DAPz/BAD8/wQA/f8BAAAAAQD+/wMA/P8DAP//AAAAAAAAAAD//wMA+/8FAP3/AQABAP3/BAD8/wMA/v8BAAAA//8CAP3/AwD+/wEAAQD9/wMA/f8EAPz/BAD7/wQA/v8BAAEA/f8DAP7/AQAAAP//AgD+/wEAAAD//wIA/v8CAP7/AgD+/wEAAQD//wEA/v8CAP7/BAD7/wQA/f8CAAAA//8AAAAAAAABAP//AQD+/wEAAQD+/wMA/f8BAAEA/v8DAPz/AwD/////AwD8/wQA/P8DAP7/AgD//wAA//8BAAAAAAAAAP//AgD+/wEAAAD//wIA/v8BAAAA//8CAP3/AgD//wAAAQD+/wIA/v8BAAAA//8CAP7/AgD+/wEA//8CAP3/BAD7/wQA/v8BAAAA//8AAAEAAAD//wIA/f8DAP7/AgD+/wIA/v8CAP7/AgD+/wEAAAAAAP//AgD9/wMA/v8BAP//AgD9/wMA/v8AAAEA//8BAP//AQD//wEA//8AAAEA/v8EAPz/AgD//wAAAQAAAP//AAABAP//AQD//wEAAAD//wEA//8BAAEA/f8DAP7/AQABAP3/AwD+/wIA/////wEAAAAAAAAAAAD//wIA/v8CAP////8CAP7/AgD//wAA//8CAP3/BAD9/wAAAgD9/wMA/v8BAP//AQAAAP//AQAAAP//AgD9/wMA/f8EAPz/AwD+/wEAAAAAAAAAAAD//wIA/f8EAP3/AAABAAAA//8CAP7/AQAAAP//AQAAAAAA//8BAP//AQAAAP//AQAAAP//AQAAAP//AgD9/wMA/v8BAP//AQAAAP//AQD//wIA/v8CAP3/BAD9/wEAAAD//wEAAQD9/wMA/f8CAAAA/v8DAP3/AgD//wAAAQD+/wIA/v8CAP7/AQAAAP//AgD+/wEAAAAAAP//AwD7/wUA/f8BAAEA/v8BAAEA/v8DAP3/AgD//wEA//8BAP//AQD//wEA//8CAP3/BAD7/wQA/////wIA/v8AAAIA/v8CAP3/BAD7/wUA/P8DAP3/AwD9/wMA/v8AAAIA/v8CAP7/AgD+/wIA//8AAAEA/v8CAP7/AgD//wAAAAD//wEAAAAAAAAA//8BAP7/BAD7/wUA/P8CAAAA//8BAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAAAA//8CAP3/AwD+/wEA//8CAP3/AwD+/wAAAwD8/wIAAAD//wIA/////wIA/v8CAP7/AgD+/wEAAAAAAAAAAAAAAP//AgD+/wIA//8AAAAA//8CAP7/AgD+/wEA//8CAP3/AwD9/wMA/v8BAP7/AwD9/wMA/f8CAP//AQD+/wIA//8BAP//AQD+/wMA/v8BAAAA//8BAAAA//8CAP7/AQAAAP//AgD+/wIA/v8CAP//AAAAAAEA//8BAP//AAABAAAA//8BAP//AQD//wEA//8BAP//AQAAAP//AQD//wEAAAD//wIA/f8CAAAA//8BAAAA//8BAP//AAABAP//AQD//wAAAAAAAAEA/v8CAP//AQD//wAAAAABAP7/AwD9/wIAAAD+/wIA//8BAP//AgD9/wMA/f8DAP7/AgD+/wEAAAAAAAEA/v8CAP7/AgD//wAAAAAAAAAAAAAAAP//AgD/////AgD9/wQA/f8BAAAAAAAAAAEA/f8DAP////8DAP3/AQABAP7/AgD//wAAAQD+/wMA/f8CAP7/AQABAP7/AwD7/wYA+v8FAP3/AQABAP7/AgD+/wMA/f8CAP7/AwD+/wEA//8BAP//AQAAAP7/BQD5/wcA+v8FAPz/AwD+/wIA/v8BAAAA//8DAPv/BQD8/wMA/////wEAAAAAAAAAAAD//wIA/f8DAP7/AQAAAP//AQAAAP//AgD+/wIA/v8BAAEA/f8EAPz/AwD+/wEA//8CAP7/AQD//wEA//8CAP7/AQAAAP//AgD+/wEAAAAAAAAAAAAAAAAAAAD//wIA/f8EAPz/AwD+/wEA//8CAP7/AgD+/wEAAQD+/wEAAQD+/wIA/////wIA//8AAAAAAAAAAAAAAAD//wEAAAAAAP//AgD9/wMA/v8BAP//AQAAAP//AQD//wEA//8BAP//AQD//wEA//8BAP//AQAAAP7/AwD9/wMA/v8BAP7/AwD9/wMA/v8BAP//AAABAP//AQD//wAAAAABAP//AAAAAAAAAQD//wEA/v8CAAAA/v8EAPv/BAD9/wIAAAD+/wMA/P8DAP//AAAAAP//AQD//wIA/f8DAP3/AwD9/wMA/v8BAAAA//8BAAAA//8CAP3/AwD9/wQA+/8FAPv/BQD8/wMA/v8BAAAA//8BAP//AgD+/wEAAAD//wIA/v8BAAEA/f8DAP3/AgAAAP//AQD//wAAAQD//wEA//8BAP//AQD//wEA/v8DAP3/AgAAAP7/AwD9/wIAAAD//wEAAAD//wIA/f8DAP7/AgD9/wQA+/8FAPz/AgAAAP//AgD9/wIA//8BAP//AQD//wEA//8BAP//AQD//wIA/f8DAP3/AgD//wAAAQD+/wIA/v8BAAEA/v8CAP7/AgD+/wMA/P8DAP//AAABAP7/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA/v8CAP3/BAD8/wMA/v8BAAAAAAD//wEAAAAAAAAAAAD//wEAAAAAAAAA//8BAP//AgD+/wEA//8CAP3/AwD9/wMA/f8EAPv/BAD+/wAAAQD//wEA//8BAP//AAABAP//AQD//wEAAAD//wEA//8BAP//AgD9/wMA/v8AAAIA/f8DAP7/AAACAP3/AwD+/wEA//8BAP//AQAAAP//AQAAAP7/AwD9/wMA/v8AAAEA//8BAP//AAAAAAEA//8AAAEA/v8CAP//AAAAAAEA/v8DAPz/BAD9/wEAAQD+/wEAAQD9/wQA/P8DAP7/AQAAAAAAAAAAAAAAAAAAAAAAAQD+/wIA/////wIA/v8BAAAA//8BAP//AQD//wEA//8BAAAA/v8EAPz/AwD///7/BAD8/wMA/////wIA/v8CAP////8CAP7/AgD+/wIA/v8CAP////8CAP7/AwD9/wIA/v8CAP//AAABAP7/AwD9/wEAAQD+/wMA/f8CAP//AAAAAAEA/v8DAPz/BAD9/wIA/v8CAP7/AgD//wAAAAD//wIA/v8CAP7/AQAAAAAA//8CAP7/AgD+/wIA/v8CAP7/AwD8/wUA+v8GAPv/AwD//wAAAAAAAAAA//8DAPv/BQD9/wAAAgD9/wMA/v8BAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAAAAAAAAAP//AQAAAAAAAAD//wEA//8CAP3/AwD+/wAAAgD+/wEAAAD//wIA/v8CAP7/AgD/////AwD8/wUA/P8CAP//AQD//wIA/f8DAP3/AwD+/wAAAQD+/wMA/f8DAP3/AgD//wAAAQD//wEA//8BAP7/AwD+/wEA//8AAAEA//8CAPz/BAD9/wIA//8AAAEA/v8DAPz/BAD9/wIA//8AAAEA/v8CAP7/AgD//wEA/f8EAPz/BAD+////AgD//wAAAQD//wAAAQD//wEA//8BAP7/AwD+/wEA"}}catch(o){return void addTest("audiopreload",!1)}r.setAttribute("preload","auto"),r.style.cssText="display:none",docElement.appendChild(r),setTimeout(function(){r.addEventListener("loadeddata",A,!1),e=setTimeout(A,t)},0)}),Modernizr.addTest("canvasblending",function(){if(Modernizr.canvas===!1)return!1;var A=createElement("canvas").getContext("2d");try{A.globalCompositeOperation="screen"}catch(e){}return"screen"===A.globalCompositeOperation});var canvas=createElement("canvas");Modernizr.addTest("todataurljpeg",function(){return!!Modernizr.canvas&&0===canvas.toDataURL("image/jpeg").indexOf("data:image/jpeg")}),Modernizr.addTest("todataurlpng",function(){return!!Modernizr.canvas&&0===canvas.toDataURL("image/png").indexOf("data:image/png")}),Modernizr.addTest("todataurlwebp",function(){var A=!1;try{A=!!Modernizr.canvas&&0===canvas.toDataURL("image/webp").indexOf("data:image/webp")}catch(e){}return A}),Modernizr.addTest("canvaswinding",function(){if(Modernizr.canvas===!1)return!1;var A=createElement("canvas").getContext("2d");return A.rect(0,0,10,10),A.rect(2,2,6,6),A.isPointInPath(5,5,"evenodd")===!1}),Modernizr.addTest("bgpositionshorthand",function(){var A=createElement("a"),e=A.style,t="right 10px bottom 10px";return e.cssText="background-position: "+t+";",e.backgroundPosition===t}),Modernizr.addTest("csscalc",function(){var A="width:",e="calc(10px);",t=createElement("a");return t.style.cssText=A+prefixes.join(e+A),!!t.style.length}),Modernizr.addTest("cubicbezierrange",function(){var A=createElement("a");return A.style.cssText=prefixes.join("transition-timing-function:cubic-bezier(1,0,0,1.1); "),!!A.style.length}),Modernizr.addTest("cssgradients",function(){for(var A,e="background-image:",t="gradient(linear,left top,right bottom,from(#9f9),to(white));",r="",n=0,o=prefixes.length-1;o>n;n++)A=0===n?"to ":"",r+=e+prefixes[n]+"linear-gradient("+A+"left top, #9f9, white);";Modernizr._config.usePrefixes&&(r+=e+"-webkit-"+t);var i=createElement("a"),d=i.style;return d.cssText=r,(""+d.backgroundImage).indexOf("gradient")>-1}),Modernizr.addTest("multiplebgs",function(){var A=createElement("a").style;return A.cssText="background:url(https://),url(https://),red url(https://)",/(url\s*\(.*?){3}/.test(A.background)}),Modernizr.addTest("opacity",function(){var A=createElement("a").style;return A.cssText=prefixes.join("opacity:.55;"),/^0.55$/.test(A.opacity)}),Modernizr.addTest("csspointerevents",function(){var A=createElement("a").style;return A.cssText="pointer-events:auto","auto"===A.pointerEvents}),Modernizr.addTest("csspositionsticky",function(){var A="position:",e="sticky",t=createElement("a"),r=t.style;return r.cssText=A+prefixes.join(e+";"+A).slice(0,-A.length),-1!==r.position.indexOf(e)}),Modernizr.addTest("cssremunit",function(){var A=createElement("a").style;try{A.fontSize="3rem"}catch(e){}return/rem/.test(A.fontSize)}),Modernizr.addTest("rgba",function(){var A=createElement("a").style;return A.cssText="background-color:rgba(150,255,150,.5)",(""+A.backgroundColor).indexOf("rgba")>-1}),Modernizr.addTest("preserve3d",function(){var A,e,t=window.CSS,r=!1;return t&&t.supports&&t.supports("(transform-style: preserve-3d)")?!0:(A=createElement("a"),e=createElement("a"),A.style.cssText="display: block; transform-style: preserve-3d; transform-origin: right; transform: rotateY(40deg);",e.style.cssText="display: block; width: 9px; height: 1px; background: #000; transform-origin: right; transform: rotateY(40deg);",A.appendChild(e),docElement.appendChild(A),r=e.getBoundingClientRect(),docElement.removeChild(A),r=r.width&&r.width<4)}),Modernizr.addTest("dataset",function(){var A=createElement("div");return A.setAttribute("data-a-b","c"),!(!A.dataset||"c"!==A.dataset.aB)}),Modernizr.addTest("hidden","hidden"in createElement("a")),Modernizr.addTest("outputelem","value"in createElement("output")),Modernizr.addTest("progressbar",createElement("progress").max!==undefined),Modernizr.addTest("meter",createElement("meter").max!==undefined),Modernizr.addTest("ruby",function(){function A(A,e){var t;return window.getComputedStyle?t=document.defaultView.getComputedStyle(A,null).getPropertyValue(e):A.currentStyle&&(t=A.currentStyle[e]),t}function e(){docElement.removeChild(t),t=null,r=null,n=null}var t=createElement("ruby"),r=createElement("rt"),n=createElement("rp"),o="display",i="fontSize";return t.appendChild(n),t.appendChild(r),docElement.appendChild(t),"none"==A(n,o)||"ruby"==A(t,o)&&"ruby-text"==A(r,o)||"6pt"==A(n,i)&&"6pt"==A(r,i)?(e(),!0):(e(),!1)}),Modernizr.addTest("template","content"in createElement("template")),Modernizr.addTest("time","valueAsDate"in createElement("time")),Modernizr.addTest("texttrackapi","function"==typeof createElement("video").addTextTrack),Modernizr.addTest("track","kind"in createElement("track")),Modernizr.addTest("unknownelements",function(){var A=createElement("a");return A.innerHTML=" ",1===A.childNodes.length}),Modernizr.addTest("capture","capture"in createElement("input")),Modernizr.addTest("fileinput",function(){if(navigator.userAgent.match(/(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/))return!1;var A=createElement("input");return A.type="file",!A.disabled}),Modernizr.addTest("fileinputdirectory",function(){var A=createElement("input"),e="directory";if(A.type="file",e in A)return!0;for(var t=0,r=domPrefixes.length;r>t;t++)if(domPrefixes[t]+e in A)return!0;return!1}),Modernizr.addTest("formattribute",function(){var A,e=createElement("form"),t=createElement("input"),r=createElement("div"),n="formtest"+(new Date).getTime(),o=!1;e.id=n;try{t.setAttribute("form",n)}catch(i){document.createAttribute&&(A=document.createAttribute("form"),A.nodeValue=n,t.setAttributeNode(A))}return r.appendChild(e),r.appendChild(t),docElement.appendChild(r),o=e.elements&&1===e.elements.length&&t.form==e,r.parentNode.removeChild(r),o}),Modernizr.addTest("placeholder","placeholder"in createElement("input")&&"placeholder"in createElement("textarea")),Modernizr.addTest("sandbox","sandbox"in createElement("iframe")),Modernizr.addTest("seamless","seamless"in createElement("iframe")),Modernizr.addTest("srcdoc","srcdoc"in createElement("iframe")),Modernizr.addAsyncTest(function(){if(!Modernizr.canvas)return!1;var A=new Image,e=createElement("canvas"),t=e.getContext("2d");A.onload=function(){addTest("apng",function(){return"undefined"==typeof e.getContext?!1:(t.drawImage(A,0,0),0===t.getImageData(0,0,1,1).data[3])})},A.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACGFjVEwAAAABAAAAAcMq2TYAAAANSURBVAiZY2BgYPgPAAEEAQB9ssjfAAAAGmZjVEwAAAAAAAAAAQAAAAEAAAAAAAAAAAD6A+gBAbNU+2sAAAARZmRBVAAAAAEImWNgYGBgAAAABQAB6MzFdgAAAABJRU5ErkJggg=="}),Modernizr.addAsyncTest(function(){var A,e,t,r=createElement("img"),n="sizes"in r;!n&&"srcset"in r?(e="data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw==",A="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",t=function(){addTest("sizes",2==r.width)},r.onload=t,r.onerror=t,r.setAttribute("sizes","9px"),r.srcset=A+" 1w,"+e+" 8w",r.src=A):addTest("sizes",n)}),Modernizr.addTest("srcset","srcset"in createElement("img")),Modernizr.addTest("inputformaction",!!("formAction"in createElement("input")),{aliases:["input-formaction"]}),Modernizr.addTest("inputformenctype",!!("formEnctype"in createElement("input")),{aliases:["input-formenctype"]}),Modernizr.addTest("inputformmethod",!!("formMethod"in createElement("input"))),Modernizr.addTest("inputformtarget",!!("formtarget"in createElement("input")),{aliases:["input-formtarget"]}),Modernizr.addTest("scriptasync","async"in createElement("script")),Modernizr.addTest("scriptdefer","defer"in createElement("script")),Modernizr.addTest("stylescoped","scoped"in createElement("style")),Modernizr.addTest("inlinesvg",function(){var A=createElement("div");return A.innerHTML="","http://www.w3.org/2000/svg"==("undefined"!=typeof SVGRect&&A.firstChild&&A.firstChild.namespaceURI)}),Modernizr.addTest("textareamaxlength",!!("maxLength"in createElement("textarea"))),Modernizr.addAsyncTest(function(){function A(i){n++,clearTimeout(e);var d=i&&"playing"===i.type||0!==o.currentTime;return!d&&r>n?void(e=setTimeout(A,t)):(o.removeEventListener("playing",A,!1),addTest("videoautoplay",d),void(o.parentNode&&o.parentNode.removeChild(o)))}var e,t=200,r=5,n=0,o=createElement("video"),i=o.style;if(!(Modernizr.video&&"autoplay"in o))return void addTest("videoautoplay",!1);i.position="absolute",i.height=0,i.width=0;try{if(Modernizr.video.ogg)o.src="data:video/ogg;base64,T2dnUwACAAAAAAAAAABmnCATAAAAAHDEixYBKoB0aGVvcmEDAgEAAQABAAAQAAAQAAAAAAAFAAAAAQAAAAAAAAAAAGIAYE9nZ1MAAAAAAAAAAAAAZpwgEwEAAAACrA7TDlj///////////////+QgXRoZW9yYSsAAABYaXBoLk9yZyBsaWJ0aGVvcmEgMS4xIDIwMDkwODIyIChUaHVzbmVsZGEpAQAAABoAAABFTkNPREVSPWZmbXBlZzJ0aGVvcmEtMC4yOYJ0aGVvcmG+zSj3uc1rGLWpSUoQc5zmMYxSlKQhCDGMYhCEIQhAAAAAAAAAAAAAEW2uU2eSyPxWEvx4OVts5ir1aKtUKBMpJFoQ/nk5m41mUwl4slUpk4kkghkIfDwdjgajQYC8VioUCQRiIQh8PBwMhgLBQIg4FRba5TZ5LI/FYS/Hg5W2zmKvVoq1QoEykkWhD+eTmbjWZTCXiyVSmTiSSCGQh8PB2OBqNBgLxWKhQJBGIhCHw8HAyGAsFAiDgUCw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDAwPEhQUFQ0NDhESFRUUDg4PEhQVFRUOEBETFBUVFRARFBUVFRUVEhMUFRUVFRUUFRUVFRUVFRUVFRUVFRUVEAwLEBQZGxwNDQ4SFRwcGw4NEBQZHBwcDhATFhsdHRwRExkcHB4eHRQYGxwdHh4dGxwdHR4eHh4dHR0dHh4eHRALChAYKDM9DAwOExo6PDcODRAYKDlFOA4RFh0zV1A+EhYlOkRtZ00YIzdAUWhxXDFATldneXhlSFxfYnBkZ2MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEhIVGRoaGhoSFBYaGhoaGhUWGRoaGhoaGRoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhESFh8kJCQkEhQYIiQkJCQWGCEkJCQkJB8iJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQREhgvY2NjYxIVGkJjY2NjGBo4Y2NjY2MvQmNjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRISEhUXGBkbEhIVFxgZGxwSFRcYGRscHRUXGBkbHB0dFxgZGxwdHR0YGRscHR0dHhkbHB0dHR4eGxwdHR0eHh4REREUFxocIBERFBcaHCAiERQXGhwgIiUUFxocICIlJRcaHCAiJSUlGhwgIiUlJSkcICIlJSUpKiAiJSUlKSoqEBAQFBgcICgQEBQYHCAoMBAUGBwgKDBAFBgcICgwQEAYHCAoMEBAQBwgKDBAQEBgICgwQEBAYIAoMEBAQGCAgAfF5cdH1e3Ow/L66wGmYnfIUbwdUTe3LMRbqON8B+5RJEvcGxkvrVUjTMrsXYhAnIwe0dTJfOYbWrDYyqUrz7dw/JO4hpmV2LsQQvkUeGq1BsZLx+cu5iV0e0eScJ91VIQYrmqfdVSK7GgjOU0oPaPOu5IcDK1mNvnD+K8LwS87f8Jx2mHtHnUkTGAurWZlNQa74ZLSFH9oF6FPGxzLsjQO5Qe0edcpttd7BXBSqMCL4k/4tFrHIPuEQ7m1/uIWkbDMWVoDdOSuRQ9286kvVUlQjzOE6VrNguN4oRXYGkgcnih7t13/9kxvLYKQezwLTrO44sVmMPgMqORo1E0sm1/9SludkcWHwfJwTSybR4LeAz6ugWVgRaY8mV/9SluQmtHrzsBtRF/wPY+X0JuYTs+ltgrXAmlk10xQHmTu9VSIAk1+vcvU4ml2oNzrNhEtQ3CysNP8UeR35wqpKUBdGdZMSjX4WVi8nJpdpHnbhzEIdx7mwf6W1FKAiucMXrWUWVjyRf23chNtR9mIzDoT/6ZLYailAjhFlZuvPtSeZ+2oREubDoWmT3TguY+JHPdRVSLKxfKH3vgNqJ/9emeEYikGXDFNzaLjvTeGAL61mogOoeG3y6oU4rW55ydoj0lUTSR/mmRhPmF86uwIfzp3FtiufQCmppaHDlGE0r2iTzXIw3zBq5hvaTldjG4CPb9wdxAme0SyedVKczJ9AtYbgPOzYKJvZZImsN7ecrxWZg5dR6ZLj/j4qpWsIA+vYwE+Tca9ounMIsrXMB4Stiib2SPQtZv+FVIpfEbzv8ncZoLBXc3YBqTG1HsskTTotZOYTG+oVUjLk6zhP8bg4RhMUNtfZdO7FdpBuXzhJ5Fh8IKlJG7wtD9ik8rWOJxy6iQ3NwzBpQ219mlyv+FLicYs2iJGSE0u2txzed++D61ZWCiHD/cZdQVCqkO2gJpdpNaObhnDfAPrT89RxdWFZ5hO3MseBSIlANppdZNIV/Rwe5eLTDvkfWKzFnH+QJ7m9QWV1KdwnuIwTNtZdJMoXBf74OhRnh2t+OTGL+AVUnIkyYY+QG7g9itHXyF3OIygG2s2kud679ZWKqSFa9n3IHD6MeLv1lZ0XyduRhiDRtrNnKoyiFVLcBm0ba5Yy3fQkDh4XsFE34isVpOzpa9nR8iCpS4HoxG2rJpnRhf3YboVa1PcRouh5LIJv/uQcPNd095ickTaiGBnWLKVWRc0OnYTSyex/n2FofEPnDG8y3PztHrzOLK1xo6RAml2k9owKajOC0Wr4D5x+3nA0UEhK2m198wuBHF3zlWWVKWLN1CHzLClUfuoYBcx4b1llpeBKmbayaR58njtE9onD66lUcsg0Spm2snsb+8HaJRn4dYcLbCuBuYwziB8/5U1C1DOOz2gZjSZtrLJk6vrLF3hwY4Io9xuT/ruUFRSBkNtUzTOWhjh26irLEPx4jPZL3Fo3QrReoGTTM21xYTT9oFdhTUIvjqTkfkvt0bzgVUjq/hOYY8j60IaO/0AzRBtqkTS6R5ellZd5uKdzzhb8BFlDdAcrwkE0rbXTOPB+7Y0FlZO96qFL4Ykg21StJs8qIW7h16H5hGiv8V2Cflau7QVDepTAHa6Lgt6feiEvJDM21StJsmOH/hynURrKxvUpQ8BH0JF7BiyG2qZpnL/7AOU66gt+reLEXY8pVOCQvSsBtqZTNM8bk9ohRcwD18o/WVkbvrceVKRb9I59IEKysjBeTMmmbA21xu/6iHadLRxuIzkLpi8wZYmmbbWi32RVAUjruxWlJ//iFxE38FI9hNKOoCdhwf5fDe4xZ81lgREhK2m1j78vW1CqkuMu/AjBNK210kzRUX/B+69cMMUG5bYrIeZxVSEZISmkzbXOi9yxwIfPgdsov7R71xuJ7rFcACjG/9PzApqFq7wEgzNJm2suWESPuwrQvejj7cbnQxMkxpm21lUYJL0fKmogPPqywn7e3FvB/FCNxPJ85iVUkCE9/tLKx31G4CgNtWTTPFhMvlu8G4/TrgaZttTChljfNJGgOT2X6EqpETy2tYd9cCBI4lIXJ1/3uVUllZEJz4baqGF64yxaZ+zPLYwde8Uqn1oKANtUrSaTOPHkhvuQP3bBlEJ/LFe4pqQOHUI8T8q7AXx3fLVBgSCVpMba55YxN3rv8U1Dv51bAPSOLlZWebkL8vSMGI21lJmmeVxPRwFlZF1CpqCN8uLwymaZyjbXHCRytogPN3o/n74CNykfT+qqRv5AQlHcRxYrC5KvGmbbUwmZY/29BvF6C1/93x4WVglXDLFpmbapmF89HKTogRwqqSlGbu+oiAkcWFbklC6Zhf+NtTLFpn8oWz+HsNRVSgIxZWON+yVyJlE5tq/+GWLTMutYX9ekTySEQPLVNQQ3OfycwJBM0zNtZcse7CvcKI0V/zh16Dr9OSA21MpmmcrHC+6pTAPHPwoit3LHHqs7jhFNRD6W8+EBGoSEoaZttTCZljfduH/fFisn+dRBGAZYtMzbVMwvul/T/crK1NQh8gN0SRRa9cOux6clC0/mDLFpmbarmF8/e6CopeOLCNW6S/IUUg3jJIYiAcDoMcGeRbOvuTPjXR/tyo79LK3kqqkbxkkMRAOB0GODPItnX3Jnxro/25Ud+llbyVVSN4ySGIgHA6DHBnkWzr7kz410f7cqO/Syt5KqpFVJwn6gBEvBM0zNtZcpGOEPiysW8vvRd2R0f7gtjhqUvXL+gWVwHm4XJDBiMpmmZtrLfPwd/IugP5+fKVSysH1EXreFAcEhelGmbbUmZY4Xdo1vQWVnK19P4RuEnbf0gQnR+lDCZlivNM22t1ESmopPIgfT0duOfQrsjgG4tPxli0zJmF5trdL1JDUIUT1ZXSqQDeR4B8mX3TrRro/2McGeUvLtwo6jIEKMkCUXWsLyZROd9P/rFYNtXPBli0z398iVUlVKAjFlY437JXImUTm2r/4ZYtMy61hf16RPJIU9nZ1MABAwAAAAAAAAAZpwgEwIAAABhp658BScAAAAAAADnUFBQXIDGXLhwtttNHDhw5OcpQRMETBEwRPduylKVB0HRdF0A";else{if(!Modernizr.video.h264)return void addTest("videoautoplay",!1);o.src="data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAAs1tZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0OCByMjYwMSBhMGNkN2QzIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTEgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEwIHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAD2WIhAA3//728P4FNjuZQQAAAu5tb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAAZAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACGHRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAEAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAgAAAAIAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAGQAAAAAAAEAAAAAAZBtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAACgAAAAEAFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAE7bWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAA+3N0YmwAAACXc3RzZAAAAAAAAAABAAAAh2F2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAgACAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAxYXZjQwFkAAr/4QAYZ2QACqzZX4iIhAAAAwAEAAADAFA8SJZYAQAGaOvjyyLAAAAAGHN0dHMAAAAAAAAAAQAAAAEAAAQAAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAABRzdHN6AAAAAAAAAsUAAAABAAAAFHN0Y28AAAAAAAAAAQAAADAAAABidWR0YQAAAFptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAAC1pbHN0AAAAJal0b28AAAAdZGF0YQAAAAEAAAAATGF2ZjU2LjQwLjEwMQ=="}}catch(d){return void addTest("videoautoplay",!1)}o.setAttribute("autoplay",""),o.style.cssText="display:none",docElement.appendChild(o),setTimeout(function(){o.addEventListener("playing",A,!1),e=setTimeout(A,t)},0)}),Modernizr.addTest("videoloop","loop"in createElement("video")),Modernizr.addTest("videopreload","preload"in createElement("video")),Modernizr.addAsyncTest(function(){if(Modernizr.webglextensions=!1,Modernizr.webgl){var A,e,t;try{A=createElement("canvas"),e=A.getContext("webgl")||A.getContext("experimental-webgl"),t=e.getSupportedExtensions()}catch(r){return}e!==undefined&&(Modernizr.webglextensions=new Boolean(!0));for(var n=-1,o=t.length;++nxx yy ",A=e.offsetHeight>e.offsetWidth}),A}),Modernizr.addTest("touchevents",function(){var A;if("ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch)A=!0;else{var e=["@media (",prefixes.join("touch-enabled),("),"heartz",")","{#modernizr{top:9px;position:absolute}}"].join("");testStyles(e,function(e){A=9===e.offsetTop})}return A}),Modernizr.addTest("unicoderange",function(){return Modernizr.testStyles('@font-face{font-family:"unicodeRange";src:local("Arial");unicode-range:U+0020,U+002E}#modernizr span{font-size:20px;display:inline-block;font-family:"unicodeRange",monospace}#modernizr .mono{font-family:monospace}',function(A){for(var e=[".",".","m","m"],t=0;t=9;return e||t}();blacklist?Modernizr.addTest("fontface",!1):testStyles('@font-face {font-family:"font";src:url("https://")}',function(A,e){var t=document.getElementById("smodernizr"),r=t.sheet||t.styleSheet,n=r?r.cssRules&&r.cssRules[0]?r.cssRules[0].cssText:r.cssText||"":"",o=/src/i.test(n)&&0===n.indexOf(e.split(" ")[0]);Modernizr.addTest("fontface",o)}),testStyles('#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}',function(A){Modernizr.addTest("generatedcontent",A.offsetHeight>=6)}),Modernizr.addTest("cssinvalid",function(){return testStyles("#modernizr input{height:0;border:0;padding:0;margin:0;width:10px} #modernizr input:invalid{width:50px}",function(A){var e=createElement("input");return e.required=!0,A.appendChild(e),e.clientWidth>10})}),testStyles("#modernizr div {width:100px} #modernizr :last-child{width:200px;display:block}",function(A){Modernizr.addTest("lastchild",A.lastChild.offsetWidth>A.firstChild.offsetWidth)},2),testStyles("#modernizr div {width:1px} #modernizr div:nth-child(2n) {width:2px;}",function(A){for(var e=A.getElementsByTagName("div"),t=!0,r=0;5>r;r++)t=t&&e[r].offsetWidth===r%2+1;Modernizr.addTest("nthchild",t)},5),testStyles("#modernizr{overflow: scroll; width: 40px; height: 40px; }#"+prefixes.join("scrollbar{width:10px} #modernizr::").split("#").slice(1).join("#")+"scrollbar{width:10px}",function(A){Modernizr.addTest("cssscrollbar","scrollWidth"in A&&30==A.scrollWidth)}),Modernizr.addTest("siblinggeneral",function(){return testStyles("#modernizr div {width:100px} #modernizr div ~ div {width:200px;display:block}",function(A){return 200==A.lastChild.offsetWidth},2)}),testStyles("#modernizr{position: absolute; top: -10em; visibility:hidden; font: normal 10px arial;}#subpixel{float: left; font-size: 33.3333%;}",function(A){var e=A.firstChild;e.innerHTML="This is a text written in Arial",Modernizr.addTest("subpixelfont",window.getComputedStyle?"44px"!==window.getComputedStyle(e,null).getPropertyValue("width"):!1)},1,["subpixel"]),Modernizr.addTest("cssvalid",function(){return testStyles("#modernizr input{height:0;border:0;padding:0;margin:0;width:10px} #modernizr input:valid{width:50px}",function(A){var e=createElement("input");return A.appendChild(e),e.clientWidth>10})}),Modernizr.addTest("details",function(){var A,e=createElement("details");return"open"in e?(testStyles("#modernizr details{display:block}",function(t){t.appendChild(e),e.innerHTML="a b",A=e.offsetHeight,e.open=!0,A=A!=e.offsetHeight}),A):!1}),Modernizr.addTest("oninput",function(){var A,e=createElement("input");if(e.setAttribute("oninput","return"),hasEvent("oninput",docElement)||"function"==typeof e.oninput)return!0;try{var t=document.createEvent("KeyboardEvent");A=!1;var r=function(e){A=!0,e.preventDefault(),e.stopPropagation()};t.initKeyEvent("keypress",!0,!0,window,!1,!1,!1,!1,0,"e".charCodeAt(0)),docElement.appendChild(e),e.addEventListener("input",r,!1),e.focus(),e.dispatchEvent(t),e.removeEventListener("input",r,!1),docElement.removeChild(e)}catch(n){A=!1}return A}),Modernizr.addTest("formvalidation",function(){var A=createElement("form");if(!("checkValidity"in A&&"addEventListener"in A))return!1;
-if("reportValidity"in A)return!0;var e,t=!1;return Modernizr.formvalidationapi=!0,A.addEventListener("submit",function(A){(!window.opera||window.operamini)&&A.preventDefault(),A.stopPropagation()},!1),A.innerHTML='',testStyles("#modernizr form{position:absolute;top:-99999em}",function(r){r.appendChild(A),e=A.getElementsByTagName("input")[0],e.addEventListener("invalid",function(A){t=!0,A.preventDefault(),A.stopPropagation()},!1),Modernizr.formvalidationmessage=!!e.validationMessage,A.getElementsByTagName("button")[0].click()}),t});var inputElem=createElement("input"),inputattrs="autocomplete autofocus list placeholder max min multiple pattern required step".split(" "),attrs={};Modernizr.input=function(A){for(var e=0,t=A.length;t>e;e++)attrs[A[e]]=!!(A[e]in inputElem);return attrs.list&&(attrs.list=!(!createElement("datalist")||!window.HTMLDataListElement)),attrs}(inputattrs),Modernizr.addTest("datalistelem",Modernizr.input.list);var inputtypes="search tel url email datetime date month week time datetime-local number range color".split(" "),inputs={};Modernizr.inputtypes=function(A){for(var e,t,r,n=A.length,o="1)",i=0;n>i;i++)inputElem.setAttribute("type",e=A[i]),r="text"!==inputElem.type&&"style"in inputElem,r&&(inputElem.value=o,inputElem.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(e)&&inputElem.style.WebkitAppearance!==undefined?(docElement.appendChild(inputElem),t=document.defaultView,r=t.getComputedStyle&&"textfield"!==t.getComputedStyle(inputElem,null).WebkitAppearance&&0!==inputElem.offsetHeight,docElement.removeChild(inputElem)):/^(search|tel)$/.test(e)||(r=/^(url|email)$/.test(e)?inputElem.checkValidity&&inputElem.checkValidity()===!1:inputElem.value!=o)),inputs[A[i]]=!!r;return inputs}(inputtypes),Modernizr.addTest("localizednumber",function(){if(!Modernizr.inputtypes.number)return!1;if(!Modernizr.formvalidation)return!1;var A,e=createElement("div"),t=getBody(),r=function(){return docElement.insertBefore(t,docElement.firstElementChild||docElement.firstChild)}();e.innerHTML='';var n=e.childNodes[0];r.appendChild(e),n.focus();try{document.execCommand("SelectAll",!1),document.execCommand("InsertText",!1,"1,1")}catch(o){}return A="number"===n.type&&1.1===n.valueAsNumber&&n.checkValidity(),r.removeChild(e),t.fake&&r.parentNode.removeChild(r),A});var modElem={elem:createElement("modernizr")};Modernizr._q.push(function(){delete modElem.elem}),Modernizr.addTest("csschunit",function(){var A,e=modElem.elem.style;try{e.fontSize="3ch",A=-1!==e.fontSize.indexOf("ch")}catch(t){A=!1}return A}),Modernizr.addTest("cssexunit",function(){var A,e=modElem.elem.style;try{e.fontSize="3ex",A=-1!==e.fontSize.indexOf("ex")}catch(t){A=!1}return A}),Modernizr.addTest("hsla",function(){var A=createElement("a").style;return A.cssText="background-color:hsla(120,40%,100%,.5)",contains(A.backgroundColor,"rgba")||contains(A.backgroundColor,"hsla")}),testStyles("#modernizr { height: 50vh; }",function(A){var e=parseInt(window.innerHeight/2,10),t=parseInt(computedStyle(A,null,"height"),10);Modernizr.addTest("cssvhunit",t==e)}),testStyles("#modernizr { width: 50vw; }",function(A){var e=parseInt(window.innerWidth/2,10),t=parseInt(computedStyle(A,null,"width"),10);Modernizr.addTest("cssvwunit",t==e)}),Modernizr.addTest("bdi",function(){var A=createElement("div"),e=createElement("bdi");e.innerHTML="إ",A.appendChild(e),docElement.appendChild(A);var t="rtl"===computedStyle(e,null,"direction");return docElement.removeChild(A),t}),testStyles("#modernizr1{width: 50vmax}#modernizr2{width:50px;height:50px;overflow:scroll}#modernizr3{position:fixed;top:0;left:0;bottom:0;right:0}",function(A){var e=A.childNodes[2],t=A.childNodes[1],r=A.childNodes[0],n=parseInt((t.offsetWidth-t.clientWidth)/2,10),o=r.clientWidth/100,i=r.clientHeight/100,d=parseInt(50*Math.max(o,i),10),a=parseInt(computedStyle(e,null,"width"),10);Modernizr.addTest("cssvmaxunit",roundedEquals(d,a)||roundedEquals(d,a-n))},3),testStyles("#modernizr1{width: 50vm;width:50vmin}#modernizr2{width:50px;height:50px;overflow:scroll}#modernizr3{position:fixed;top:0;left:0;bottom:0;right:0}",function(A){var e=A.childNodes[2],t=A.childNodes[1],r=A.childNodes[0],n=parseInt((t.offsetWidth-t.clientWidth)/2,10),o=r.clientWidth/100,i=r.clientHeight/100,d=parseInt(50*Math.min(o,i),10),a=parseInt(computedStyle(e,null,"width"),10);Modernizr.addTest("cssvminunit",roundedEquals(d,a)||roundedEquals(d,a-n))},3);var testXhrType=function(A){if("undefined"==typeof XMLHttpRequest)return!1;var e=new XMLHttpRequest;e.open("get","/",!0);try{e.responseType=A}catch(t){return!1}return"response"in e&&e.responseType==A};Modernizr.addTest("xhrresponsetypearraybuffer",testXhrType("arraybuffer")),Modernizr.addTest("xhrresponsetypedocument",testXhrType("document")),Modernizr.addTest("xhrresponsetypeblob",testXhrType("blob")),Modernizr.addTest("xhrresponsetypejson",testXhrType("json")),Modernizr.addTest("xhrresponsetypetext",testXhrType("text"));var toStringFn={}.toString;Modernizr.addTest("svgclippaths",function(){return!!document.createElementNS&&/SVGClipPath/.test(toStringFn.call(document.createElementNS("http://www.w3.org/2000/svg","clipPath")))}),Modernizr.addTest("svgforeignobject",function(){return!!document.createElementNS&&/SVGForeignObject/.test(toStringFn.call(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")))}),Modernizr.addTest("smil",function(){return!!document.createElementNS&&/SVGAnimate/.test(toStringFn.call(document.createElementNS("http://www.w3.org/2000/svg","animate")))});var mStyle={style:modElem.elem.style};Modernizr._q.unshift(function(){delete mStyle.style});var testProp=ModernizrProto.testProp=function(A,e,t){return testProps([A],undefined,e,t)};Modernizr.addTest("textshadow",testProp("textShadow","1px 1px")),ModernizrProto.testAllProps=testPropsAll;var prefixed=ModernizrProto.prefixed=function(A,e,t){return 0===A.indexOf("@")?atRule(A):(-1!=A.indexOf("-")&&(A=cssToDOM(A)),e?testPropsAll(A,e,t):testPropsAll(A,"pfx"))},prefixedCSS=ModernizrProto.prefixedCSS=function(A){var e=prefixed(A);return e&&domToCSS(e)};Modernizr.addTest("batteryapi",!!prefixed("battery",navigator),{aliases:["battery-api"]});var crypto=prefixed("crypto",window);Modernizr.addTest("crypto",!!prefixed("subtle",crypto)),Modernizr.addTest("dart",!!prefixed("startDart",navigator)),Modernizr.addTest("fullscreen",!(!prefixed("exitFullscreen",document,!1)&&!prefixed("cancelFullScreen",document,!1))),Modernizr.addTest("gamepads",!!prefixed("getGamepads",navigator)),Modernizr.addAsyncTest(function(){var A;try{A=prefixed("indexedDB",window)}catch(e){}if(A){var t="modernizr-"+Math.random(),r=A.open(t);r.onerror=function(){r.error&&"InvalidStateError"===r.error.name?addTest("indexeddb",!1):(addTest("indexeddb",!0),detectDeleteDatabase(A,t))},r.onsuccess=function(){addTest("indexeddb",!0),detectDeleteDatabase(A,t)}}else addTest("indexeddb",!1)}),Modernizr.addAsyncTest(function(){var A,e,t,r,n="detect-blob-support",o=!1;try{A=prefixed("indexedDB",window)}catch(i){}if(!Modernizr.indexeddb||!Modernizr.indexeddb.deletedatabase)return!1;try{A.deleteDatabase(n).onsuccess=function(){e=A.open(n,1),e.onupgradeneeded=function(){e.result.createObjectStore("store")},e.onsuccess=function(){t=e.result;try{r=t.transaction("store","readwrite").objectStore("store").put(new Blob,"key"),r.onsuccess=function(){o=!0},r.onerror=function(){o=!1}}catch(i){o=!1}finally{addTest("indexeddbblob",o),t.close(),A.deleteDatabase(n)}}}}catch(i){addTest("indexeddbblob",!1)}}),Modernizr.addTest("intl",!!prefixed("Intl",window)),Modernizr.addTest("pagevisibility",!!prefixed("hidden",document,!1)),Modernizr.addTest("performance",!!prefixed("performance",window)),Modernizr.addTest("pointerlock",!!prefixed("exitPointerLock",document)),Modernizr.addTest("quotamanagement",function(){var A=prefixed("temporaryStorage",navigator),e=prefixed("persistentStorage",navigator);return!(!A||!e)}),Modernizr.addTest("requestanimationframe",!!prefixed("requestAnimationFrame",window),{aliases:["raf"]}),Modernizr.addTest("vibrate",!!prefixed("vibrate",navigator)),Modernizr.addTest("webintents",!!prefixed("startActivity",navigator)),Modernizr.addTest("lowbattery",function(){var A=.2,e=prefixed("battery",navigator);return!!(e&&!e.charging&&e.level<=A)});var crypto=prefixed("crypto",window),supportsGetRandomValues;if(crypto&&"getRandomValues"in crypto&&"Uint32Array"in window){var array=new Uint32Array(10),values=crypto.getRandomValues(array);supportsGetRandomValues=values&&is(values[0],"number")}Modernizr.addTest("getrandomvalues",!!supportsGetRandomValues),Modernizr.addTest("backgroundblendmode",prefixed("backgroundBlendMode","text")),Modernizr.addTest("objectfit",!!prefixed("objectFit"),{aliases:["object-fit"]}),Modernizr.addTest("regions",function(){if(isSVG)return!1;var A=prefixed("flowFrom"),e=prefixed("flowInto"),t=!1;if(!A||!e)return t;var r=createElement("iframe"),n=createElement("div"),o=createElement("div"),i=createElement("div"),d="modernizr_flow_for_regions_check";o.innerText="M",n.style.cssText="top: 150px; left: 150px; padding: 0px;",i.style.cssText="width: 50px; height: 50px; padding: 42px;",i.style[A]=d,n.appendChild(o),n.appendChild(i),docElement.appendChild(n);var a,s,l=o.getBoundingClientRect();return o.style[e]=d,a=o.getBoundingClientRect(),s=parseInt(a.left-l.left,10),docElement.removeChild(n),42==s?t=!0:(docElement.appendChild(r),l=r.getBoundingClientRect(),r.style[e]=d,a=r.getBoundingClientRect(),l.height>0&&l.height!==a.height&&0===a.height&&(t=!0)),o=i=n=r=undefined,t}),Modernizr.addTest("wrapflow",function(){var A=prefixed("wrapFlow");if(!A||isSVG)return!1;var e=A.replace(/([A-Z])/g,function(A,e){return"-"+e.toLowerCase()}).replace(/^ms-/,"-ms-"),t=createElement("div"),r=createElement("div"),n=createElement("span");r.style.cssText="position: absolute; left: 50px; width: 100px; height: 20px;"+e+":end;",n.innerText="X",t.appendChild(r),t.appendChild(n),docElement.appendChild(t);var o=n.offsetLeft;return docElement.removeChild(t),r=n=t=undefined,150==o}),Modernizr.addTest("filesystem",!!prefixed("requestFileSystem",window)),Modernizr.addTest("requestautocomplete",!!prefixed("requestAutocomplete",createElement("form"))),Modernizr.addTest("speechrecognition",!!prefixed("SpeechRecognition",window));var url=prefixed("URL",window,!1);url=url&&window[url],Modernizr.addTest("bloburls",url&&"revokeObjectURL"in url&&"createObjectURL"in url),Modernizr.addAsyncTest(function(){function A(){addTest("transferables",!1),e()}function e(){d&&URL.revokeObjectURL(d),a&&a.terminate(),n&&clearTimeout(n)}var t=!!(Modernizr.blobconstructor&&Modernizr.bloburls&&Modernizr.webworkers&&Modernizr.typedarrays);if(!t)return addTest("transferables",!1);try{var r,n,o='var hello = "world"',i=new Blob([o],{type:"text/javascript"}),d=URL.createObjectURL(i),a=new Worker(d);a.onerror=A,n=setTimeout(A,200),r=new ArrayBuffer(1),a.postMessage(r,[r]),addTest("transferables",0===r.byteLength),e()}catch(s){A()}}),Modernizr.addTest("getusermedia",!!prefixed("getUserMedia",navigator)),Modernizr.addTest("peerconnection",!!prefixed("RTCPeerConnection",window)),Modernizr.addTest("matchmedia",!!prefixed("matchMedia",window)),ModernizrProto.testAllProps=testAllProps,Modernizr.addTest("cssanimations",testAllProps("animationName","a",!0)),Modernizr.addTest("csspseudoanimations",function(){var A=!1;if(!Modernizr.cssanimations||!window.getComputedStyle)return A;var e=["@",Modernizr._prefixes.join("keyframes csspseudoanimations { from { font-size: 10px; } }@").replace(/\@$/,""),'#modernizr:before { content:" "; font-size:5px;',Modernizr._prefixes.join("animation:csspseudoanimations 1ms infinite;"),"}"].join("");return Modernizr.testStyles(e,function(e){A="10px"===window.getComputedStyle(e,":before").getPropertyValue("font-size")}),A}),Modernizr.addTest("appearance",testAllProps("appearance")),Modernizr.addTest("backdropfilter",testAllProps("backdropFilter")),Modernizr.addTest("backgroundcliptext",function(){return testAllProps("backgroundClip","text")}),Modernizr.addTest("bgpositionxy",function(){return testAllProps("backgroundPositionX","3px",!0)&&testAllProps("backgroundPositionY","5px",!0)}),Modernizr.addTest("bgrepeatround",testAllProps("backgroundRepeat","round")),Modernizr.addTest("bgrepeatspace",testAllProps("backgroundRepeat","space")),Modernizr.addTest("backgroundsize",testAllProps("backgroundSize","100%",!0)),Modernizr.addTest("bgsizecover",testAllProps("backgroundSize","cover")),Modernizr.addTest("borderimage",testAllProps("borderImage","url() 1",!0)),Modernizr.addTest("borderradius",testAllProps("borderRadius","0px",!0)),Modernizr.addTest("boxshadow",testAllProps("boxShadow","1px 1px",!0)),Modernizr.addTest("boxsizing",testAllProps("boxSizing","border-box",!0)&&(document.documentMode===undefined||document.documentMode>7)),function(){Modernizr.addTest("csscolumns",function(){var A=!1,e=testAllProps("columnCount");try{A=!!e,A&&(A=new Boolean(A))}catch(t){}return A});for(var A,e,t=["Width","Span","Fill","Gap","Rule","RuleColor","RuleStyle","RuleWidth","BreakBefore","BreakAfter","BreakInside"],r=0;r9)}),Modernizr.addTest("flexbox",testAllProps("flexBasis","1px",!0)),Modernizr.addTest("flexboxlegacy",testAllProps("boxDirection","reverse",!0)),Modernizr.addTest("flexboxtweener",testAllProps("flexAlign","end",!0)),Modernizr.addTest("flexwrap",testAllProps("flexWrap","wrap",!0)),Modernizr.addAsyncTest(function(){function A(){function t(){try{var A=createElement("div"),e=createElement("span"),t=A.style,r=0,n=0,o=!1,i=document.body.firstElementChild||document.body.firstChild;return A.appendChild(e),e.innerHTML="Bacon ipsum dolor sit amet jerky velit in culpa hamburger et. Laborum dolor proident, enim dolore duis commodo et strip steak. Salami anim et, veniam consectetur dolore qui tenderloin jowl velit sirloin. Et ad culpa, fatback cillum jowl ball tip ham hock nulla short ribs pariatur aute. Pig pancetta ham bresaola, ut boudin nostrud commodo flank esse cow tongue culpa. Pork belly bresaola enim pig, ea consectetur nisi. Fugiat officia turkey, ea cow jowl pariatur ullamco proident do laborum velit sausage. Magna biltong sint tri-tip commodo sed bacon, esse proident aliquip. Ullamco ham sint fugiat, velit in enim sed mollit nulla cow ut adipisicing nostrud consectetur. Proident dolore beef ribs, laborum nostrud meatball ea laboris rump cupidatat labore culpa. Shankle minim beef, velit sint cupidatat fugiat tenderloin pig et ball tip. Ut cow fatback salami, bacon ball tip et in shank strip steak bresaola. In ut pork belly sed mollit tri-tip magna culpa veniam, short ribs qui in andouille ham consequat. Dolore bacon t-bone, velit short ribs enim strip steak nulla. Voluptate labore ut, biltong swine irure jerky. Cupidatat excepteur aliquip salami dolore. Ball tip strip steak in pork dolor. Ad in esse biltong. Dolore tenderloin exercitation ad pork loin t-bone, dolore in chicken ball tip qui pig. Ut culpa tongue, sint ribeye dolore ex shank voluptate hamburger. Jowl et tempor, boudin pork chop labore ham hock drumstick consectetur tri-tip elit swine meatball chicken ground round. Proident shankle mollit dolore. Shoulder ut duis t-bone quis reprehenderit. Meatloaf dolore minim strip steak, laboris ea aute bacon beef ribs elit shank in veniam drumstick qui. Ex laboris meatball cow tongue pork belly. Ea ball tip reprehenderit pig, sed fatback boudin dolore flank aliquip laboris eu quis. Beef ribs duis beef, cow corned beef adipisicing commodo nisi deserunt exercitation. Cillum dolor t-bone spare ribs, ham hock est sirloin. Brisket irure meatloaf in, boudin pork belly sirloin ball tip. Sirloin sint irure nisi nostrud aliqua. Nostrud nulla aute, enim officia culpa ham hock. Aliqua reprehenderit dolore sunt nostrud sausage, ea boudin pork loin ut t-bone ham tempor. Tri-tip et pancetta drumstick laborum. Ham hock magna do nostrud in proident. Ex ground round fatback, venison non ribeye in.",document.body.insertBefore(A,i),t.cssText="position:absolute;top:0;left:0;width:5em;text-align:justify;text-justification:newspaper;",r=e.offsetHeight,n=e.offsetWidth,t.cssText="position:absolute;top:0;left:0;width:5em;text-align:justify;text-justification:newspaper;"+prefixes.join("hyphens:auto; "),o=e.offsetHeight!=r||e.offsetWidth!=n,document.body.removeChild(A),A.removeChild(e),o}catch(d){return!1}}function r(A,e){try{var t=createElement("div"),r=createElement("span"),n=t.style,o=0,i=!1,d=!1,a=!1,s=document.body.firstElementChild||document.body.firstChild;return n.cssText="position:absolute;top:0;left:0;overflow:visible;width:1.25em;",t.appendChild(r),document.body.insertBefore(t,s),r.innerHTML="mm",o=r.offsetHeight,r.innerHTML="m"+A+"m",d=r.offsetHeight>o,e?(r.innerHTML="m
m",o=r.offsetWidth,r.innerHTML="m"+A+"m",a=r.offsetWidth>o):a=!0,d===!0&&a===!0&&(i=!0),document.body.removeChild(t),t.removeChild(r),i}catch(l){return!1}}function n(A){try{var e,t=createElement("input"),r=createElement("div"),n="lebowski",o=!1,i=document.body.firstElementChild||document.body.firstChild;r.innerHTML=n+A+n,document.body.insertBefore(r,i),document.body.insertBefore(t,r),t.setSelectionRange?(t.focus(),t.setSelectionRange(0,0)):t.createTextRange&&(e=t.createTextRange(),e.collapse(!0),e.moveEnd("character",0),e.moveStart("character",0),e.select());try{window.find?o=window.find(n+n):(e=window.self.document.body.createTextRange(),o=e.findText(n+n))}catch(d){o=!1}return document.body.removeChild(r),document.body.removeChild(t),o}catch(d){return!1}}return document.body||document.getElementsByTagName("body")[0]?(addTest("csshyphens",function(){if(!testAllProps("hyphens","auto",!0))return!1;try{return t()}catch(A){return!1}}),addTest("softhyphens",function(){try{return r("",!0)&&r("",!1)}catch(A){return!1}}),void addTest("softhyphensfind",function(){try{return n("")&&n("")}catch(A){return!1}})):void setTimeout(A,e)}var e=300;setTimeout(A,e)}),Modernizr.addTest("cssmask",testAllProps("maskRepeat","repeat-x",!0)),Modernizr.addTest("overflowscrolling",testAllProps("overflowScrolling","touch",!0)),Modernizr.addTest("cssreflections",testAllProps("boxReflect","above",!0)),Modernizr.addTest("cssresize",testAllProps("resize","both",!0)),Modernizr.addTest("shapes",testAllProps("shapeOutside","content-box",!0)),Modernizr.addTest("textalignlast",testAllProps("textAlignLast")),Modernizr.addTest("csstransforms",function(){return-1===navigator.userAgent.indexOf("Android 2.")&&testAllProps("transform","scale(1)",!0)}),Modernizr.addTest("csstransforms3d",function(){var A=!!testAllProps("perspective","1px",!0),e=Modernizr._config.usePrefixes;if(A&&(!e||"webkitPerspective"in docElement.style)){var t,r="#modernizr{width:0;height:0}";Modernizr.supports?t="@supports (perspective: 1px)":(t="@media (transform-3d)",e&&(t+=",(-webkit-transform-3d)")),t+="{#modernizr{width:7px;height:18px;margin:0;padding:0;border:0}}",testStyles(r+t,function(e){A=7===e.offsetWidth&&18===e.offsetHeight})}return A}),Modernizr.addTest("csstransitions",testAllProps("transition","all",!0)),Modernizr.addTest("csspseudotransitions",function(){var A=!1;if(!Modernizr.csstransitions||!window.getComputedStyle)return A;var e='#modernizr:before { content:" "; font-size:5px;'+Modernizr._prefixes.join("transition:0s 100s;")+"}#modernizr.trigger:before { font-size:10px; }";return Modernizr.testStyles(e,function(e){window.getComputedStyle(e,":before").getPropertyValue("font-size"),e.className+="trigger",A="5px"===window.getComputedStyle(e,":before").getPropertyValue("font-size")}),A}),Modernizr.addTest("userselect",testAllProps("userSelect","none",!0)),testRunner(),setClasses(classes),delete ModernizrProto.addTest,delete ModernizrProto.addAsyncTest;for(var i=0;i d; d++)
+ if (
+ ((s = A[d]),
+ (l = mStyle.style[s]),
+ contains(s, '-') && (s = cssToDOM(s)),
+ mStyle.style[s] !== undefined)
+ ) {
+ if (r || is(t, 'undefined')) return (n(), 'pfx' == e ? s : !0);
+ try {
+ mStyle.style[s] = t;
+ } catch (u) {}
+ if (mStyle.style[s] != l) return (n(), 'pfx' == e ? s : !0);
+ }
+ return (n(), !1);
+ }
+ function fnBind(A, e) {
+ return function () {
+ return A.apply(e, arguments);
+ };
+ }
+ function testDOMProps(A, e, t) {
+ var r;
+ for (var n in A)
+ if (A[n] in e)
+ return t === !1
+ ? A[n]
+ : ((r = e[A[n]]), is(r, 'function') ? fnBind(r, t || e) : r);
+ return !1;
+ }
+ function testPropsAll(A, e, t, r, n) {
+ var o = A.charAt(0).toUpperCase() + A.slice(1),
+ i = (A + ' ' + cssomPrefixes.join(o + ' ') + o).split(' ');
+ return is(e, 'string') || is(e, 'undefined')
+ ? testProps(i, e, r, n)
+ : ((i = (A + ' ' + domPrefixes.join(o + ' ') + o).split(' ')),
+ testDOMProps(i, e, t));
+ }
+ function detectDeleteDatabase(A, e) {
+ var t = A.deleteDatabase(e);
+ ((t.onsuccess = function () {
+ addTest('indexeddb.deletedatabase', !0);
+ }),
+ (t.onerror = function () {
+ addTest('indexeddb.deletedatabase', !1);
+ }));
+ }
+ function testAllProps(A, e, t) {
+ return testPropsAll(A, undefined, undefined, e, t);
+ }
+ var classes = [],
+ tests = [],
+ ModernizrProto = {
+ _version: '3.5.0',
+ _config: {
+ classPrefix: '',
+ enableClasses: !0,
+ enableJSClass: !0,
+ usePrefixes: !0
+ },
+ _q: [],
+ on: function (A, e) {
+ var t = this;
+ setTimeout(function () {
+ e(t[A]);
+ }, 0);
+ },
+ addTest: function (A, e, t) {
+ tests.push({ name: A, fn: e, options: t });
+ },
+ addAsyncTest: function (A) {
+ tests.push({ name: null, fn: A });
+ }
+ },
+ Modernizr = function () {};
+ ((Modernizr.prototype = ModernizrProto),
+ (Modernizr = new Modernizr()),
+ Modernizr.addTest('applicationcache', 'applicationCache' in window),
+ Modernizr.addTest(
+ 'blobconstructor',
+ function () {
+ try {
+ return !!new Blob();
+ } catch (A) {
+ return !1;
+ }
+ },
+ { aliases: ['blob-constructor'] }
+ ),
+ Modernizr.addTest('cookies', function () {
+ try {
+ document.cookie = 'cookietest=1';
+ var A = -1 != document.cookie.indexOf('cookietest=');
+ return (
+ (document.cookie =
+ 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT'),
+ A
+ );
+ } catch (e) {
+ return !1;
+ }
+ }),
+ Modernizr.addTest(
+ 'cors',
+ 'XMLHttpRequest' in window && 'withCredentials' in new XMLHttpRequest()
+ ),
+ Modernizr.addTest('customprotocolhandler', function () {
+ if (!navigator.registerProtocolHandler) return !1;
+ try {
+ navigator.registerProtocolHandler('thisShouldFail');
+ } catch (A) {
+ return A instanceof TypeError;
+ }
+ return !1;
+ }),
+ Modernizr.addTest(
+ 'customevent',
+ 'CustomEvent' in window && 'function' == typeof window.CustomEvent
+ ),
+ Modernizr.addTest(
+ 'dataview',
+ 'undefined' != typeof DataView && 'getFloat64' in DataView.prototype
+ ),
+ Modernizr.addTest('eventlistener', 'addEventListener' in window),
+ Modernizr.addTest('geolocation', 'geolocation' in navigator),
+ Modernizr.addTest('history', function () {
+ var A = navigator.userAgent;
+ return (-1 === A.indexOf('Android 2.') &&
+ -1 === A.indexOf('Android 4.0')) ||
+ -1 === A.indexOf('Mobile Safari') ||
+ -1 !== A.indexOf('Chrome') ||
+ -1 !== A.indexOf('Windows Phone') ||
+ 'file:' === location.protocol
+ ? window.history && 'pushState' in window.history
+ : !1;
+ }),
+ Modernizr.addTest(
+ 'ie8compat',
+ !window.addEventListener &&
+ !!document.documentMode &&
+ 7 === document.documentMode
+ ),
+ Modernizr.addTest(
+ 'json',
+ 'JSON' in window && 'parse' in JSON && 'stringify' in JSON
+ ),
+ Modernizr.addTest('notification', function () {
+ if (!window.Notification || !window.Notification.requestPermission)
+ return !1;
+ if ('granted' === window.Notification.permission) return !0;
+ try {
+ new window.Notification('');
+ } catch (A) {
+ if ('TypeError' === A.name) return !1;
+ }
+ return !0;
+ }),
+ Modernizr.addTest('postmessage', 'postMessage' in window),
+ Modernizr.addTest(
+ 'queryselector',
+ 'querySelector' in document && 'querySelectorAll' in document
+ ),
+ Modernizr.addTest('serviceworker', 'serviceWorker' in navigator),
+ Modernizr.addTest(
+ 'svg',
+ !!document.createElementNS &&
+ !!document.createElementNS('http://www.w3.org/2000/svg', 'svg')
+ .createSVGRect
+ ),
+ Modernizr.addTest('templatestrings', function () {
+ var supports;
+ try {
+ (eval('``'), (supports = !0));
+ } catch (e) {}
+ return !!supports;
+ }),
+ Modernizr.addTest('typedarrays', 'ArrayBuffer' in window));
+ var supports = !1;
+ try {
+ supports = 'WebSocket' in window && 2 === window.WebSocket.CLOSING;
+ } catch (e) {}
+ (Modernizr.addTest('websockets', supports),
+ Modernizr.addTest('xdomainrequest', 'XDomainRequest' in window),
+ Modernizr.addTest('webaudio', function () {
+ var A = 'webkitAudioContext' in window,
+ e = 'AudioContext' in window;
+ return Modernizr._config.usePrefixes ? A || e : e;
+ }));
+ var CSS = window.CSS;
+ Modernizr.addTest('cssescape', CSS ? 'function' == typeof CSS.escape : !1);
+ var newSyntax = 'CSS' in window && 'supports' in window.CSS,
+ oldSyntax = 'supportsCSS' in window;
+ (Modernizr.addTest('supports', newSyntax || oldSyntax),
+ Modernizr.addTest('target', function () {
+ var A = window.document;
+ if (!('querySelectorAll' in A)) return !1;
+ try {
+ return (A.querySelectorAll(':target'), !0);
+ } catch (e) {
+ return !1;
+ }
+ }),
+ Modernizr.addTest('microdata', 'getItems' in document),
+ Modernizr.addTest(
+ 'mutationobserver',
+ !!window.MutationObserver || !!window.WebKitMutationObserver
+ ),
+ Modernizr.addTest('picture', 'HTMLPictureElement' in window),
+ Modernizr.addTest('es5array', function () {
+ return !!(
+ Array.prototype &&
+ Array.prototype.every &&
+ Array.prototype.filter &&
+ Array.prototype.forEach &&
+ Array.prototype.indexOf &&
+ Array.prototype.lastIndexOf &&
+ Array.prototype.map &&
+ Array.prototype.some &&
+ Array.prototype.reduce &&
+ Array.prototype.reduceRight &&
+ Array.isArray
+ );
+ }),
+ Modernizr.addTest('es5date', function () {
+ var A = '2013-04-12T06:06:37.307Z',
+ e = !1;
+ try {
+ e = !!Date.parse(A);
+ } catch (t) {}
+ return !!(
+ Date.now &&
+ Date.prototype &&
+ Date.prototype.toISOString &&
+ Date.prototype.toJSON &&
+ e
+ );
+ }),
+ Modernizr.addTest('es5function', function () {
+ return !(!Function.prototype || !Function.prototype.bind);
+ }),
+ Modernizr.addTest('es5object', function () {
+ return !!(
+ Object.keys &&
+ Object.create &&
+ Object.getPrototypeOf &&
+ Object.getOwnPropertyNames &&
+ Object.isSealed &&
+ Object.isFrozen &&
+ Object.isExtensible &&
+ Object.getOwnPropertyDescriptor &&
+ Object.defineProperty &&
+ Object.defineProperties &&
+ Object.seal &&
+ Object.freeze &&
+ Object.preventExtensions
+ );
+ }),
+ Modernizr.addTest(
+ 'strictmode',
+ (function () {
+ 'use strict';
+ return !this;
+ })()
+ ),
+ Modernizr.addTest('es5string', function () {
+ return !(!String.prototype || !String.prototype.trim);
+ }),
+ Modernizr.addTest('es5syntax', function () {
+ var value,
+ obj,
+ stringAccess,
+ getter,
+ setter,
+ reservedWords,
+ zeroWidthChars;
+ try {
+ return (
+ (stringAccess = eval('"foobar"[3] === "b"')),
+ (getter = eval('({ get x(){ return 1 } }).x === 1')),
+ eval('({ set x(v){ value = v; } }).x = 1'),
+ (setter = 1 === value),
+ eval('obj = ({ if: 1 })'),
+ (reservedWords = 1 === obj['if']),
+ (zeroWidthChars = eval('_ = true')),
+ stringAccess && getter && setter && reservedWords && zeroWidthChars
+ );
+ } catch (ignore) {
+ return !1;
+ }
+ }),
+ Modernizr.addTest('es5undefined', function () {
+ var A, e;
+ try {
+ ((e = window.undefined),
+ (window.undefined = 12345),
+ (A = 'undefined' == typeof window.undefined),
+ (window.undefined = e));
+ } catch (t) {
+ return !1;
+ }
+ return A;
+ }),
+ Modernizr.addTest('es5', function () {
+ return !!(
+ Modernizr.es5array &&
+ Modernizr.es5date &&
+ Modernizr.es5function &&
+ Modernizr.es5object &&
+ Modernizr.strictmode &&
+ Modernizr.es5string &&
+ Modernizr.json &&
+ Modernizr.es5syntax &&
+ Modernizr.es5undefined
+ );
+ }),
+ Modernizr.addTest(
+ 'es6array',
+ !!(
+ Array.prototype &&
+ Array.prototype.copyWithin &&
+ Array.prototype.fill &&
+ Array.prototype.find &&
+ Array.prototype.findIndex &&
+ Array.prototype.keys &&
+ Array.prototype.entries &&
+ Array.prototype.values &&
+ Array.from &&
+ Array.of
+ )
+ ),
+ Modernizr.addTest('generators', function () {
+ try {
+ new Function('function* test() {}')();
+ } catch (A) {
+ return !1;
+ }
+ return !0;
+ }),
+ Modernizr.addTest(
+ 'es6math',
+ !!(
+ Math &&
+ Math.clz32 &&
+ Math.cbrt &&
+ Math.imul &&
+ Math.sign &&
+ Math.log10 &&
+ Math.log2 &&
+ Math.log1p &&
+ Math.expm1 &&
+ Math.cosh &&
+ Math.sinh &&
+ Math.tanh &&
+ Math.acosh &&
+ Math.asinh &&
+ Math.atanh &&
+ Math.hypot &&
+ Math.trunc &&
+ Math.fround
+ )
+ ),
+ Modernizr.addTest(
+ 'es6number',
+ !!(
+ Number.isFinite &&
+ Number.isInteger &&
+ Number.isSafeInteger &&
+ Number.isNaN &&
+ Number.parseInt &&
+ Number.parseFloat &&
+ Number.isInteger(Number.MAX_SAFE_INTEGER) &&
+ Number.isInteger(Number.MIN_SAFE_INTEGER) &&
+ Number.isFinite(Number.EPSILON)
+ )
+ ),
+ Modernizr.addTest(
+ 'es6string',
+ !!(
+ String.fromCodePoint &&
+ String.raw &&
+ String.prototype.codePointAt &&
+ String.prototype.repeat &&
+ String.prototype.startsWith &&
+ String.prototype.endsWith &&
+ String.prototype.includes
+ )
+ ),
+ Modernizr.addTest('promises', function () {
+ return (
+ 'Promise' in window &&
+ 'resolve' in window.Promise &&
+ 'reject' in window.Promise &&
+ 'all' in window.Promise &&
+ 'race' in window.Promise &&
+ (function () {
+ var A;
+ return (
+ new window.Promise(function (e) {
+ A = e;
+ }),
+ 'function' == typeof A
+ );
+ })()
+ );
+ }),
+ Modernizr.addTest('devicemotion', 'DeviceMotionEvent' in window),
+ Modernizr.addTest('deviceorientation', 'DeviceOrientationEvent' in window),
+ Modernizr.addTest(
+ 'es6object',
+ !!(Object.assign && Object.is && Object.setPrototypeOf)
+ ),
+ Modernizr.addTest(
+ 'filereader',
+ !!(window.File && window.FileList && window.FileReader)
+ ),
+ Modernizr.addTest('beacon', 'sendBeacon' in navigator),
+ Modernizr.addTest('lowbandwidth', function () {
+ var A = navigator.connection || { type: 0 };
+ return 3 == A.type || 4 == A.type || /^[23]g$/.test(A.type);
+ }),
+ Modernizr.addTest('eventsource', 'EventSource' in window),
+ Modernizr.addTest('fetch', 'fetch' in window),
+ Modernizr.addTest(
+ 'xhrresponsetype',
+ (function () {
+ if ('undefined' == typeof XMLHttpRequest) return !1;
+ var A = new XMLHttpRequest();
+ return (A.open('get', '/', !0), 'response' in A);
+ })()
+ ),
+ Modernizr.addTest(
+ 'xhr2',
+ 'XMLHttpRequest' in window && 'withCredentials' in new XMLHttpRequest()
+ ),
+ Modernizr.addTest('speechsynthesis', 'SpeechSynthesisUtterance' in window),
+ Modernizr.addTest('localstorage', function () {
+ var A = 'modernizr';
+ try {
+ return (localStorage.setItem(A, A), localStorage.removeItem(A), !0);
+ } catch (e) {
+ return !1;
+ }
+ }),
+ Modernizr.addTest('sessionstorage', function () {
+ var A = 'modernizr';
+ try {
+ return (sessionStorage.setItem(A, A), sessionStorage.removeItem(A), !0);
+ } catch (e) {
+ return !1;
+ }
+ }),
+ Modernizr.addTest('websqldatabase', 'openDatabase' in window),
+ Modernizr.addTest('svgfilters', function () {
+ var A = !1;
+ try {
+ A =
+ 'SVGFEColorMatrixElement' in window &&
+ 2 == SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE;
+ } catch (e) {}
+ return A;
+ }),
+ Modernizr.addTest('urlparser', function () {
+ var A;
+ try {
+ return (
+ (A = new URL('http://modernizr.com/')),
+ 'http://modernizr.com/' === A.href
+ );
+ } catch (e) {
+ return !1;
+ }
+ }),
+ Modernizr.addTest('websocketsbinary', function () {
+ var A,
+ e = 'https:' == location.protocol ? 'wss' : 'ws';
+ if ('WebSocket' in window) {
+ if ((A = 'binaryType' in WebSocket.prototype)) return A;
+ try {
+ return !!new WebSocket(e + '://.').binaryType;
+ } catch (t) {}
+ }
+ return !1;
+ }),
+ Modernizr.addTest('atobbtoa', 'atob' in window && 'btoa' in window, {
+ aliases: ['atob-btoa']
+ }),
+ Modernizr.addTest('framed', window.location != top.location),
+ Modernizr.addTest('sharedworkers', 'SharedWorker' in window),
+ Modernizr.addTest('webworkers', 'Worker' in window));
+ var prefixes = ModernizrProto._config.usePrefixes
+ ? ' -webkit- -moz- -o- -ms- '.split(' ')
+ : ['', ''];
+ ((ModernizrProto._prefixes = prefixes),
+ Modernizr.addTest('contains', is(String.prototype.contains, 'function')));
+ var docElement = document.documentElement;
+ (Modernizr.addTest(
+ 'contextmenu',
+ 'contextMenu' in docElement && 'HTMLMenuItemElement' in window
+ ),
+ Modernizr.addTest('cssall', 'all' in docElement.style),
+ Modernizr.addTest('willchange', 'willChange' in docElement.style),
+ Modernizr.addTest('classlist', 'classList' in docElement),
+ Modernizr.addTest('documentfragment', function () {
+ return (
+ 'createDocumentFragment' in document && 'appendChild' in docElement
+ );
+ }));
+ var isSVG = 'svg' === docElement.nodeName.toLowerCase(),
+ html5;
+ isSVG ||
+ !(function (A, e) {
+ function t(A, e) {
+ var t = A.createElement('p'),
+ r = A.getElementsByTagName('head')[0] || A.documentElement;
+ return (
+ (t.innerHTML = 'x'),
+ r.insertBefore(t.lastChild, r.firstChild)
+ );
+ }
+ function r() {
+ var A = h.elements;
+ return 'string' == typeof A ? A.split(' ') : A;
+ }
+ function n(A, e) {
+ var t = h.elements;
+ ('string' != typeof t && (t = t.join(' ')),
+ 'string' != typeof A && (A = A.join(' ')),
+ (h.elements = t + ' ' + A),
+ s(e));
+ }
+ function o(A) {
+ var e = E[A[f]];
+ return (e || ((e = {}), g++, (A[f] = g), (E[g] = e)), e);
+ }
+ function i(A, t, r) {
+ if ((t || (t = e), c)) return t.createElement(A);
+ r || (r = o(t));
+ var n;
+ return (
+ (n = r.cache[A]
+ ? r.cache[A].cloneNode()
+ : m.test(A)
+ ? (r.cache[A] = r.createElem(A)).cloneNode()
+ : r.createElem(A)),
+ !n.canHaveChildren || w.test(A) || n.tagUrn
+ ? n
+ : r.frag.appendChild(n)
+ );
+ }
+ function d(A, t) {
+ if ((A || (A = e), c)) return A.createDocumentFragment();
+ t = t || o(A);
+ for (
+ var n = t.frag.cloneNode(), i = 0, d = r(), a = d.length;
+ a > i;
+ i++
+ )
+ n.createElement(d[i]);
+ return n;
+ }
+ function a(A, e) {
+ (e.cache ||
+ ((e.cache = {}),
+ (e.createElem = A.createElement),
+ (e.createFrag = A.createDocumentFragment),
+ (e.frag = e.createFrag())),
+ (A.createElement = function (t) {
+ return h.shivMethods ? i(t, A, e) : e.createElem(t);
+ }),
+ (A.createDocumentFragment = Function(
+ 'h,f',
+ 'return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&(' +
+ r()
+ .join()
+ .replace(/[\w\-:]+/g, function (A) {
+ return (
+ e.createElem(A), e.frag.createElement(A), 'c("' + A + '")'
+ );
+ }) +
+ ');return n}'
+ )(h, e.frag)));
+ }
+ function s(A) {
+ A || (A = e);
+ var r = o(A);
+ return (
+ !h.shivCSS ||
+ l ||
+ r.hasCSS ||
+ (r.hasCSS = !!t(
+ A,
+ 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}'
+ )),
+ c || a(A, r),
+ A
+ );
+ }
+ var l,
+ c,
+ u = '3.7.3',
+ p = A.html5 || {},
+ w =
+ /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,
+ m =
+ /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,
+ f = '_html5shiv',
+ g = 0,
+ E = {};
+ !(function () {
+ try {
+ var A = e.createElement('a');
+ ((A.innerHTML = ' '),
+ (l = 'hidden' in A),
+ (c =
+ 1 == A.childNodes.length ||
+ (function () {
+ e.createElement('a');
+ var A = e.createDocumentFragment();
+ return (
+ 'undefined' == typeof A.cloneNode ||
+ 'undefined' == typeof A.createDocumentFragment ||
+ 'undefined' == typeof A.createElement
+ );
+ })()));
+ } catch (t) {
+ ((l = !0), (c = !0));
+ }
+ })();
+ var h = {
+ elements:
+ p.elements ||
+ 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video',
+ version: u,
+ shivCSS: p.shivCSS !== !1,
+ supportsUnknownElements: c,
+ shivMethods: p.shivMethods !== !1,
+ type: 'default',
+ shivDocument: s,
+ createElement: i,
+ createDocumentFragment: d,
+ addElements: n
+ };
+ ((A.html5 = h),
+ s(e),
+ 'object' == typeof module && module.exports && (module.exports = h));
+ })('undefined' != typeof window ? window : this, document);
+ var omPrefixes = 'Moz O ms Webkit',
+ domPrefixes = ModernizrProto._config.usePrefixes
+ ? omPrefixes.toLowerCase().split(' ')
+ : [];
+ ModernizrProto._domPrefixes = domPrefixes;
+ var hasOwnProp;
+ (!(function () {
+ var A = {}.hasOwnProperty;
+ hasOwnProp =
+ is(A, 'undefined') || is(A.call, 'undefined')
+ ? function (A, e) {
+ return e in A && is(A.constructor.prototype[e], 'undefined');
+ }
+ : function (e, t) {
+ return A.call(e, t);
+ };
+ })(),
+ (ModernizrProto._l = {}),
+ (ModernizrProto.on = function (A, e) {
+ (this._l[A] || (this._l[A] = []),
+ this._l[A].push(e),
+ Modernizr.hasOwnProperty(A) &&
+ setTimeout(function () {
+ Modernizr._trigger(A, Modernizr[A]);
+ }, 0));
+ }),
+ (ModernizrProto._trigger = function (A, e) {
+ if (this._l[A]) {
+ var t = this._l[A];
+ (setTimeout(function () {
+ var A, r;
+ for (A = 0; A < t.length; A++) (r = t[A])(e);
+ }, 0),
+ delete this._l[A]);
+ }
+ }),
+ Modernizr._q.push(function () {
+ ModernizrProto.addTest = addTest;
+ }),
+ Modernizr.addAsyncTest(function () {
+ var A = new Image();
+ ((A.onerror = function () {
+ addTest('exiforientation', !1, { aliases: ['exif-orientation'] });
+ }),
+ (A.onload = function () {
+ addTest('exiforientation', 2 !== A.width, {
+ aliases: ['exif-orientation']
+ });
+ }),
+ (A.src =
+ 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAAABABIBAwABAAAABgASAAAAAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAABAAIDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/iiiigD/2Q=='));
+ }),
+ Modernizr.addAsyncTest(function () {
+ function A() {
+ (clearTimeout(e),
+ window.removeEventListener('deviceproximity', A),
+ addTest('proximity', !0));
+ }
+ var e,
+ t = 300;
+ 'ondeviceproximity' in window && 'onuserproximity' in window
+ ? (window.addEventListener('deviceproximity', A),
+ (e = setTimeout(function () {
+ (window.removeEventListener('deviceproximity', A),
+ addTest('proximity', !1));
+ }, t)))
+ : addTest('proximity', !1);
+ }),
+ Modernizr.addAsyncTest(function () {
+ var A = new Image();
+ ((A.onload = A.onerror =
+ function () {
+ addTest('jpeg2000', 1 == A.width);
+ }),
+ (A.src =
+ 'data:image/jp2;base64,/0//UQAyAAAAAAABAAAAAgAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAEBwEBBwEBBwEBBwEB/1IADAAAAAEAAAQEAAH/XAAEQED/ZAAlAAFDcmVhdGVkIGJ5IE9wZW5KUEVHIHZlcnNpb24gMi4wLjD/kAAKAAAAAABYAAH/UwAJAQAABAQAAf9dAAUBQED/UwAJAgAABAQAAf9dAAUCQED/UwAJAwAABAQAAf9dAAUDQED/k8+kEAGvz6QQAa/PpBABr994EAk//9k='));
+ }),
+ Modernizr.addAsyncTest(function () {
+ var A = new Image();
+ ((A.onload = A.onerror =
+ function () {
+ addTest('jpegxr', 1 == A.width, { aliases: ['jpeg-xr'] });
+ }),
+ (A.src =
+ 'data:image/vnd.ms-photo;base64,SUm8AQgAAAAFAAG8AQAQAAAASgAAAIC8BAABAAAAAQAAAIG8BAABAAAAAQAAAMC8BAABAAAAWgAAAMG8BAABAAAAHwAAAAAAAAAkw91vA07+S7GFPXd2jckNV01QSE9UTwAZAYBxAAAAABP/gAAEb/8AAQAAAQAAAA=='));
+ }),
+ Modernizr.addAsyncTest(function () {
+ var A = new Image();
+ ((A.onerror = function () {
+ addTest('webpalpha', !1, { aliases: ['webp-alpha'] });
+ }),
+ (A.onload = function () {
+ addTest('webpalpha', 1 == A.width, { aliases: ['webp-alpha'] });
+ }),
+ (A.src =
+ 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA=='));
+ }),
+ Modernizr.addAsyncTest(function () {
+ var A = new Image();
+ ((A.onerror = function () {
+ addTest('webpanimation', !1, { aliases: ['webp-animation'] });
+ }),
+ (A.onload = function () {
+ addTest('webpanimation', 1 == A.width, {
+ aliases: ['webp-animation']
+ });
+ }),
+ (A.src =
+ 'data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA'));
+ }),
+ Modernizr.addAsyncTest(function () {
+ function A(A, e, t) {
+ function r(e) {
+ var r = e && 'load' === e.type ? 1 == n.width : !1,
+ o = 'webp' === A;
+ (addTest(A, o && r ? new Boolean(r) : r), t && t(e));
+ }
+ var n = new Image();
+ ((n.onerror = r), (n.onload = r), (n.src = e));
+ }
+ var e = [
+ {
+ uri: 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=',
+ name: 'webp'
+ },
+ {
+ uri: 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA==',
+ name: 'webp.alpha'
+ },
+ {
+ uri: 'data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA',
+ name: 'webp.animation'
+ },
+ {
+ uri: 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=',
+ name: 'webp.lossless'
+ }
+ ],
+ t = e.shift();
+ A(t.name, t.uri, function (t) {
+ if (t && 'load' === t.type)
+ for (var r = 0; r < e.length; r++) A(e[r].name, e[r].uri);
+ });
+ }),
+ Modernizr.addTest(
+ 'svgasimg',
+ document.implementation.hasFeature(
+ 'http://www.w3.org/TR/SVG11/feature#Image',
+ '1.1'
+ )
+ ),
+ Modernizr.addAsyncTest(function () {
+ function A() {
+ var A = new Image();
+ ((A.onerror = function () {
+ (addTest('datauri', !0),
+ (Modernizr.datauri = new Boolean(!0)),
+ (Modernizr.datauri.over32kb = !1));
+ }),
+ (A.onload = function () {
+ (addTest('datauri', !0),
+ (Modernizr.datauri = new Boolean(!0)),
+ (Modernizr.datauri.over32kb = 1 == A.width && 1 == A.height));
+ }));
+ for (
+ var e = 'R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
+ e.length < 33e3;
+ )
+ e = '\r\n' + e;
+ A.src = 'data:image/gif;base64,' + e;
+ }
+ -1 !== navigator.userAgent.indexOf('MSIE 7.') &&
+ setTimeout(function () {
+ addTest('datauri', !1);
+ }, 10);
+ var e = new Image();
+ ((e.onerror = function () {
+ addTest('datauri', !1);
+ }),
+ (e.onload = function () {
+ 1 == e.width && 1 == e.height ? A() : addTest('datauri', !1);
+ }),
+ (e.src =
+ 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='));
+ }),
+ Modernizr.addAsyncTest(function () {
+ function A() {
+ (addTest('blobworkers', !1), e());
+ }
+ function e() {
+ (d && r.revokeObjectURL(d), i && i.terminate(), a && clearTimeout(a));
+ }
+ try {
+ var t = window.BlobBuilder,
+ r = window.URL;
+ Modernizr._config.usePrefix &&
+ ((t =
+ t ||
+ window.MozBlobBuilder ||
+ window.WebKitBlobBuilder ||
+ window.MSBlobBuilder ||
+ window.OBlobBuilder),
+ (r =
+ r ||
+ window.MozURL ||
+ window.webkitURL ||
+ window.MSURL ||
+ window.OURL));
+ var n,
+ o,
+ i,
+ d,
+ a,
+ s = 'Modernizr',
+ l = 'this.onmessage=function(e){postMessage(e.data)}';
+ try {
+ n = new Blob([l], { type: 'text/javascript' });
+ } catch (c) {}
+ (n || ((o = new t()), o.append(l), (n = o.getBlob())),
+ (d = r.createObjectURL(n)),
+ (i = new Worker(d)),
+ (i.onmessage = function (A) {
+ (addTest('blobworkers', s === A.data), e());
+ }),
+ (i.onerror = A),
+ (a = setTimeout(A, 200)),
+ i.postMessage(s));
+ } catch (c) {
+ A();
+ }
+ }),
+ Modernizr.addAsyncTest(function () {
+ try {
+ var A = 'Modernizr',
+ e = new Worker(
+ 'data:text/javascript;base64,dGhpcy5vbm1lc3NhZ2U9ZnVuY3Rpb24oZSl7cG9zdE1lc3NhZ2UoZS5kYXRhKX0='
+ );
+ ((e.onmessage = function (t) {
+ (e.terminate(), addTest('dataworkers', A === t.data), (e = null));
+ }),
+ (e.onerror = function () {
+ (addTest('dataworkers', !1), (e = null));
+ }),
+ setTimeout(function () {
+ addTest('dataworkers', !1);
+ }, 200),
+ e.postMessage(A));
+ } catch (t) {
+ setTimeout(function () {
+ addTest('dataworkers', !1);
+ }, 0);
+ }
+ }));
+ var cssomPrefixes = ModernizrProto._config.usePrefixes
+ ? omPrefixes.split(' ')
+ : [];
+ ModernizrProto._cssomPrefixes = cssomPrefixes;
+ var atRule = function (A) {
+ var e,
+ t = prefixes.length,
+ r = window.CSSRule;
+ if ('undefined' == typeof r) return undefined;
+ if (!A) return !1;
+ if (
+ ((A = A.replace(/^@/, '')),
+ (e = A.replace(/-/g, '_').toUpperCase() + '_RULE'),
+ e in r)
+ )
+ return '@' + A;
+ for (var n = 0; t > n; n++) {
+ var o = prefixes[n],
+ i = o.toUpperCase() + '_' + e;
+ if (i in r) return '@-' + o.toLowerCase() + '-' + A;
+ }
+ return !1;
+ };
+ ModernizrProto.atRule = atRule;
+ var hasEvent = (function () {
+ function A(A, t) {
+ var r;
+ return A
+ ? ((t && 'string' != typeof t) || (t = createElement(t || 'div')),
+ (A = 'on' + A),
+ (r = A in t),
+ !r &&
+ e &&
+ (t.setAttribute || (t = createElement('div')),
+ t.setAttribute(A, ''),
+ (r = 'function' == typeof t[A]),
+ t[A] !== undefined && (t[A] = undefined),
+ t.removeAttribute(A)),
+ r)
+ : !1;
+ }
+ var e = !('onblur' in document.documentElement);
+ return A;
+ })();
+ ((ModernizrProto.hasEvent = hasEvent),
+ Modernizr.addTest('ambientlight', hasEvent('devicelight', window)),
+ Modernizr.addTest('hashchange', function () {
+ return hasEvent('hashchange', window) === !1
+ ? !1
+ : document.documentMode === undefined || document.documentMode > 7;
+ }),
+ Modernizr.addTest('inputsearchevent', hasEvent('search')),
+ Modernizr.addTest('pointerevents', function () {
+ var A = !1,
+ e = domPrefixes.length;
+ for (A = Modernizr.hasEvent('pointerdown'); e-- && !A;)
+ hasEvent(domPrefixes[e] + 'pointerdown') && (A = !0);
+ return A;
+ }));
+ var prefixedCSSValue = function (A, e) {
+ var t = !1,
+ r = createElement('div'),
+ n = r.style;
+ if (A in n) {
+ var o = domPrefixes.length;
+ for (n[A] = e, t = n[A]; o-- && !t;)
+ ((n[A] = '-' + domPrefixes[o] + '-' + e), (t = n[A]));
+ }
+ return ('' === t && (t = !1), t);
+ };
+ ((ModernizrProto.prefixedCSSValue = prefixedCSSValue),
+ Modernizr.addTest('audio', function () {
+ var A = createElement('audio'),
+ e = !1;
+ try {
+ ((e = !!A.canPlayType),
+ e &&
+ ((e = new Boolean(e)),
+ (e.ogg = A.canPlayType('audio/ogg; codecs="vorbis"').replace(
+ /^no$/,
+ ''
+ )),
+ (e.mp3 = A.canPlayType('audio/mpeg; codecs="mp3"').replace(
+ /^no$/,
+ ''
+ )),
+ (e.opus =
+ A.canPlayType('audio/ogg; codecs="opus"') ||
+ A.canPlayType('audio/webm; codecs="opus"').replace(/^no$/, '')),
+ (e.wav = A.canPlayType('audio/wav; codecs="1"').replace(
+ /^no$/,
+ ''
+ )),
+ (e.m4a = (
+ A.canPlayType('audio/x-m4a;') || A.canPlayType('audio/aac;')
+ ).replace(/^no$/, ''))));
+ } catch (t) {}
+ return e;
+ }),
+ Modernizr.addTest('canvas', function () {
+ var A = createElement('canvas');
+ return !(!A.getContext || !A.getContext('2d'));
+ }),
+ Modernizr.addTest('canvastext', function () {
+ return Modernizr.canvas === !1
+ ? !1
+ : 'function' ==
+ typeof createElement('canvas').getContext('2d').fillText;
+ }),
+ Modernizr.addTest('contenteditable', function () {
+ if ('contentEditable' in docElement) {
+ var A = createElement('div');
+ return ((A.contentEditable = !0), 'true' === A.contentEditable);
+ }
+ }),
+ Modernizr.addTest('emoji', function () {
+ if (!Modernizr.canvastext) return !1;
+ var A = window.devicePixelRatio || 1,
+ e = 12 * A,
+ t = createElement('canvas'),
+ r = t.getContext('2d');
+ return (
+ (r.fillStyle = '#f00'),
+ (r.textBaseline = 'top'),
+ (r.font = '32px Arial'),
+ r.fillText('🐨', 0, 0),
+ 0 !== r.getImageData(e, e, 1, 1).data[0]
+ );
+ }),
+ addTest('htmlimports', 'import' in createElement('link')),
+ Modernizr.addTest('olreversed', 'reversed' in createElement('ol')),
+ Modernizr.addTest('userdata', !!createElement('div').addBehavior),
+ Modernizr.addTest('video', function () {
+ var A = createElement('video'),
+ e = !1;
+ try {
+ ((e = !!A.canPlayType),
+ e &&
+ ((e = new Boolean(e)),
+ (e.ogg = A.canPlayType('video/ogg; codecs="theora"').replace(
+ /^no$/,
+ ''
+ )),
+ (e.h264 = A.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(
+ /^no$/,
+ ''
+ )),
+ (e.webm = A.canPlayType('video/webm; codecs="vp8, vorbis"').replace(
+ /^no$/,
+ ''
+ )),
+ (e.vp9 = A.canPlayType('video/webm; codecs="vp9"').replace(
+ /^no$/,
+ ''
+ )),
+ (e.hls = A.canPlayType(
+ 'application/x-mpegURL; codecs="avc1.42E01E"'
+ ).replace(/^no$/, ''))));
+ } catch (t) {}
+ return e;
+ }),
+ Modernizr.addTest('vml', function () {
+ var A,
+ e = createElement('div'),
+ t = !1;
+ return (
+ isSVG ||
+ ((e.innerHTML = ' '),
+ (A = e.firstChild),
+ 'style' in A && (A.style.behavior = 'url(#default#VML)'),
+ (t = A ? 'object' == typeof A.adj : !0)),
+ t
+ );
+ }),
+ Modernizr.addTest('webanimations', 'animate' in createElement('div')),
+ Modernizr.addTest('webgl', function () {
+ var A = createElement('canvas'),
+ e =
+ 'probablySupportsContext' in A
+ ? 'probablySupportsContext'
+ : 'supportsContext';
+ return e in A
+ ? A[e]('webgl') || A[e]('experimental-webgl')
+ : 'WebGLRenderingContext' in window;
+ }),
+ Modernizr.addTest(
+ 'adownload',
+ !window.externalHost && 'download' in createElement('a')
+ ),
+ Modernizr.addTest('audioloop', 'loop' in createElement('audio')),
+ Modernizr.addAsyncTest(function () {
+ function A(t) {
+ clearTimeout(e);
+ var n = t !== undefined && 'loadeddata' === t.type ? !0 : !1;
+ (r.removeEventListener('loadeddata', A, !1),
+ addTest('audiopreload', n),
+ r.parentNode.removeChild(r));
+ }
+ var e,
+ t = 300,
+ r = createElement('audio'),
+ n = r.style;
+ if (!(Modernizr.audio && 'preload' in r))
+ return void addTest('audiopreload', !1);
+ ((n.position = 'absolute'), (n.height = 0), (n.width = 0));
+ try {
+ if (Modernizr.audio.mp3)
+ r.src =
+ 'data:audio/mpeg;base64,//MUxAAB6AXgAAAAAPP+c6nf//yi/6f3//MUxAMAAAIAAAjEcH//0fTX6C9Lf//0//MUxA4BeAIAAAAAAKX2/6zv//+IlR4f//MUxBMCMAH8AAAAABYWalVMQU1FMy45//MUxBUB0AH0AAAAADkuM1VVVVVVVVVV//MUxBgBUATowAAAAFVVVVVVVVVVVVVV';
+ else if (Modernizr.audio.m4a)
+ r.src =
+ 'data:audio/x-m4a;base64,AAAAGGZ0eXBNNEEgAAACAGlzb21pc28yAAAACGZyZWUAAAAfbWRhdN4EAABsaWJmYWFjIDEuMjgAAAFoAQBHAAACiG1vb3YAAABsbXZoZAAAAAB8JbCAfCWwgAAAA+gAAAAYAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAG0dHJhawAAAFx0a2hkAAAAD3wlsIB8JbCAAAAAAQAAAAAAAAAYAAAAAAAAAAAAAAAAAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAABUG1kaWEAAAAgbWRoZAAAAAB8JbCAfCWwgAAArEQAAAQAVcQAAAAAAC1oZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU291bmRIYW5kbGVyAAAAAPttaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAL9zdGJsAAAAW3N0c2QAAAAAAAAAAQAAAEttcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAACdlc2RzAAAAAAMZAAEABBFAFQAAAAABftAAAAAABQISCAYBAgAAABhzdHRzAAAAAAAAAAEAAAABAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAXAAAAAQAAABRzdGNvAAAAAAAAAAEAAAAoAAAAYHVkdGEAAABYbWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAraWxzdAAAACOpdG9vAAAAG2RhdGEAAAABAAAAAExhdmY1Mi42NC4y';
+ else if (Modernizr.audio.ogg)
+ r.src =
+ 'data:audio/ogg;base64,T2dnUwACAAAAAAAAAAD/QwAAAAAAAM2LVKsBHgF2b3JiaXMAAAAAAUSsAAAAAAAAgLsAAAAAAAC4AU9nZ1MAAAAAAAAAAAAA/0MAAAEAAADmvOe6Dy3/////////////////MgN2b3JiaXMdAAAAWGlwaC5PcmcgbGliVm9yYmlzIEkgMjAwNzA2MjIAAAAAAQV2b3JiaXMfQkNWAQAAAQAYY1QpRplS0kqJGXOUMUaZYpJKiaWEFkJInXMUU6k515xrrLm1IIQQGlNQKQWZUo5SaRljkCkFmVIQS0kldBI6J51jEFtJwdaYa4tBthyEDZpSTCnElFKKQggZU4wpxZRSSkIHJXQOOuYcU45KKEG4nHOrtZaWY4updJJK5yRkTEJIKYWSSgelU05CSDWW1lIpHXNSUmpB6CCEEEK2IIQNgtCQVQAAAQDAQBAasgoAUAAAEIqhGIoChIasAgAyAAAEoCiO4iiOIzmSY0kWEBqyCgAAAgAQAADAcBRJkRTJsSRL0ixL00RRVX3VNlVV9nVd13Vd13UgNGQVAAABAEBIp5mlGiDCDGQYCA1ZBQAgAAAARijCEANCQ1YBAAABAABiKDmIJrTmfHOOg2Y5aCrF5nRwItXmSW4q5uacc845J5tzxjjnnHOKcmYxaCa05pxzEoNmKWgmtOacc57E5kFrqrTmnHPGOaeDcUYY55xzmrTmQWo21uaccxa0pjlqLsXmnHMi5eZJbS7V5pxzzjnnnHPOOeecc6oXp3NwTjjnnHOi9uZabkIX55xzPhmne3NCOOecc84555xzzjnnnHOC0JBVAAAQAABBGDaGcacgSJ+jgRhFiGnIpAfdo8MkaAxyCqlHo6ORUuoglFTGSSmdIDRkFQAACAAAIYQUUkghhRRSSCGFFFKIIYYYYsgpp5yCCiqppKKKMsoss8wyyyyzzDLrsLPOOuwwxBBDDK20EktNtdVYY62555xrDtJaaa211koppZRSSikIDVkFAIAAABAIGWSQQUYhhRRSiCGmnHLKKaigAkJDVgEAgAAAAgAAADzJc0RHdERHdERHdERHdETHczxHlERJlERJtEzL1ExPFVXVlV1b1mXd9m1hF3bd93Xf93Xj14VhWZZlWZZlWZZlWZZlWZZlWYLQkFUAAAgAAIAQQgghhRRSSCGlGGPMMeegk1BCIDRkFQAACAAgAAAAwFEcxXEkR3IkyZIsSZM0S7M8zdM8TfREURRN01RFV3RF3bRF2ZRN13RN2XRVWbVdWbZt2dZtX5Zt3/d93/d93/d93/d93/d1HQgNWQUASAAA6EiOpEiKpEiO4ziSJAGhIasAABkAAAEAKIqjOI7jSJIkSZakSZ7lWaJmaqZneqqoAqEhqwAAQAAAAQAAAAAAKJriKabiKaLiOaIjSqJlWqKmaq4om7Lruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7rui4QGrIKAJAAANCRHMmRHEmRFEmRHMkBQkNWAQAyAAACAHAMx5AUybEsS9M8zdM8TfRET/RMTxVd0QVCQ1YBAIAAAAIAAAAAADAkw1IsR3M0SZRUS7VUTbVUSxVVT1VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTVN0zRNIDRkJQAABADAYo3B5SAhJSXl3hDCEJOeMSYhtV4hBJGS3jEGFYOeMqIMct5C4xCDHggNWREARAEAAMYgxxBzyDlHqZMSOeeodJQa5xyljlJnKcWYYs0oldhSrI1zjlJHraOUYiwtdpRSjanGAgAAAhwAAAIshEJDVgQAUQAAhDFIKaQUYow5p5xDjCnnmHOGMeYcc44556B0UirnnHROSsQYc445p5xzUjonlXNOSiehAACAAAcAgAALodCQFQFAnACAQZI8T/I0UZQ0TxRFU3RdUTRd1/I81fRMU1U90VRVU1Vt2VRVWZY8zzQ901RVzzRV1VRVWTZVVZZFVdVt03V123RV3ZZt2/ddWxZ2UVVt3VRd2zdV1/Zd2fZ9WdZ1Y/I8VfVM03U903Rl1XVtW3VdXfdMU5ZN15Vl03Vt25VlXXdl2fc103Rd01Vl2XRd2XZlV7ddWfZ903WF35VlX1dlWRh2XfeFW9eV5XRd3VdlVzdWWfZ9W9eF4dZ1YZk8T1U903RdzzRdV3VdX1dd19Y105Rl03Vt2VRdWXZl2fddV9Z1zzRl2XRd2zZdV5ZdWfZ9V5Z13XRdX1dlWfhVV/Z1WdeV4dZt4Tdd1/dVWfaFV5Z14dZ1Ybl1XRg+VfV9U3aF4XRl39eF31luXTiW0XV9YZVt4VhlWTl+4ViW3feVZXRdX1ht2RhWWRaGX/id5fZ943h1XRlu3efMuu8Mx++k+8rT1W1jmX3dWWZfd47hGDq/8OOpqq+brisMpywLv+3rxrP7vrKMruv7qiwLvyrbwrHrvvP8vrAso+z6wmrLwrDatjHcvm4sv3Acy2vryjHrvlG2dXxfeArD83R1XXlmXcf2dXTjRzh+ygAAgAEHAIAAE8pAoSErAoA4AQCPJImiZFmiKFmWKIqm6LqiaLqupGmmqWmeaVqaZ5qmaaqyKZquLGmaaVqeZpqap5mmaJqua5qmrIqmKcumasqyaZqy7LqybbuubNuiacqyaZqybJqmLLuyq9uu7Oq6pFmmqXmeaWqeZ5qmasqyaZquq3meanqeaKqeKKqqaqqqraqqLFueZ5qa6KmmJ4qqaqqmrZqqKsumqtqyaaq2bKqqbbuq7Pqybeu6aaqybaqmLZuqatuu7OqyLNu6L2maaWqeZ5qa55mmaZqybJqqK1uep5qeKKqq5ommaqqqLJumqsqW55mqJ4qq6omea5qqKsumatqqaZq2bKqqLZumKsuubfu+68qybqqqbJuqauumasqybMu+78qq7oqmKcumqtqyaaqyLduy78uyrPuiacqyaaqybaqqLsuybRuzbPu6aJqybaqmLZuqKtuyLfu6LNu678qub6uqrOuyLfu67vqucOu6MLyybPuqrPq6K9u6b+sy2/Z9RNOUZVM1bdtUVVl2Zdn2Zdv2fdE0bVtVVVs2TdW2ZVn2fVm2bWE0Tdk2VVXWTdW0bVmWbWG2ZeF2Zdm3ZVv2ddeVdV/XfePXZd3murLty7Kt+6qr+rbu+8Jw667wCgAAGHAAAAgwoQwUGrISAIgCAACMYYwxCI1SzjkHoVHKOecgZM5BCCGVzDkIIZSSOQehlJQy5yCUklIIoZSUWgshlJRSawUAABQ4AAAE2KApsThAoSErAYBUAACD41iW55miatqyY0meJ4qqqaq27UiW54miaaqqbVueJ4qmqaqu6+ua54miaaqq6+q6aJqmqaqu67q6Lpqiqaqq67qyrpumqqquK7uy7Oumqqqq68quLPvCqrquK8uybevCsKqu68qybNu2b9y6ruu+7/vCka3rui78wjEMRwEA4AkOAEAFNqyOcFI0FlhoyEoAIAMAgDAGIYMQQgYhhJBSSiGllBIAADDgAAAQYEIZKDRkRQAQJwAAGEMppJRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkgppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkqppJRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSCgCQinAAkHowoQwUGrISAEgFAACMUUopxpyDEDHmGGPQSSgpYsw5xhyUklLlHIQQUmktt8o5CCGk1FJtmXNSWosx5hgz56SkFFvNOYdSUoux5ppr7qS0VmuuNedaWqs115xzzbm0FmuuOdecc8sx15xzzjnnGHPOOeecc84FAOA0OACAHtiwOsJJ0VhgoSErAYBUAAACGaUYc8456BBSjDnnHIQQIoUYc845CCFUjDnnHHQQQqgYc8w5CCGEkDnnHIQQQgghcw466CCEEEIHHYQQQgihlM5BCCGEEEooIYQQQgghhBA6CCGEEEIIIYQQQgghhFJKCCGEEEIJoZRQAABggQMAQIANqyOcFI0FFhqyEgAAAgCAHJagUs6EQY5Bjw1BylEzDUJMOdGZYk5qMxVTkDkQnXQSGWpB2V4yCwAAgCAAIMAEEBggKPhCCIgxAABBiMwQCYVVsMCgDBoc5gHAA0SERACQmKBIu7iALgNc0MVdB0IIQhCCWBxAAQk4OOGGJ97whBucoFNU6iAAAAAAAAwA4AEA4KAAIiKaq7C4wMjQ2ODo8AgAAAAAABYA+AAAOD6AiIjmKiwuMDI0Njg6PAIAAAAAAAAAAICAgAAAAAAAQAAAAICAT2dnUwAE7AwAAAAAAAD/QwAAAgAAADuydfsFAQEBAQEACg4ODg==';
+ else {
+ if (!Modernizr.audio.wav) return void addTest('audiopreload', !1);
+ r.src =
+ 'data:audio/wav;base64,UklGRvwZAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YdgZAAAAAAEA/v8CAP//AAABAP////8DAPz/BAD9/wEAAAAAAAAAAAABAP7/AgD//wAAAQD//wAAAQD//wAAAQD+/wIA//8AAAAAAAD//wIA/v8BAAAA//8BAAAA//8BAP//AQAAAP//AQD//wEAAAD//wEA//8BAP//AQD//wEA//8BAP//AQD+/wMA/f8DAP3/AgD+/wIA/////wMA/f8CAP7/AgD+/wMA/f8CAP7/AgD//wAAAAAAAAAAAQD+/wIA/v8CAP7/AwD9/wIA/v8BAAEA/v8CAP7/AQAAAAAAAAD//wEAAAD//wIA/f8DAP7/AQD//wEAAAD//wEA//8CAP7/AQD//wIA/v8CAP7/AQAAAAAAAAD//wEAAAAAAAAA//8BAP//AgD9/wQA+/8FAPz/AgAAAP//AgD+/wEAAAD//wIA/v8CAP3/BAD8/wQA/P8DAP7/AwD8/wQA/P8DAP7/AQAAAAAA//8BAP//AgD+/wEAAAD//wIA/v8BAP//AQD//wEAAAD//wEA//8BAAAAAAAAAP//AgD+/wEAAAAAAAAAAAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AgD+/wIA/v8BAP//AQABAP7/AQD//wIA/v8CAP3/AwD/////AgD9/wMA/v8BAP//AQAAAP//AQD//wEA//8BAP//AAABAP//AAABAP//AQD//wAAAAACAP3/AwD9/wIA//8BAP//AQD//wEA//8BAP//AgD9/wMA/v8AAAIA/f8CAAAA/v8EAPv/BAD9/wIAAAD+/wQA+v8HAPr/BAD+/wEAAAD//wIA/f8EAPz/BAD7/wUA/P8EAPz/AwD+/wEAAAD//wEAAAAAAP//AgD8/wUA+/8FAPz/AwD9/wIA//8AAAEA/v8CAP//AQD//wAAAAABAP//AgD9/wMA/f8EAPz/AwD+/wAAAwD7/wUA/P8DAP7/AQAAAP//AgD+/wEAAQD+/wIA/v8BAAEA/v8CAP7/AQAAAP//AgD9/wMA/f8DAP7/AgD+/wEAAAAAAAEA//8AAAEA/v8DAP3/AgD//wEA//8BAP7/AwD9/wMA/v8BAP//AQAAAP//AgD9/wMA/v8BAP//AQAAAP//AgD+/wEAAQD+/wIA/////wIA//8AAAEA/f8DAP//AAABAP////8DAP3/AwD+/wEA//8BAP//AQAAAAAA//8BAP//AQD//wEA//8BAP//AAAAAAEA//8BAP7/AgD//wEA//8AAAAAAAAAAAAAAAD//wIA/v8BAAAA//8BAAEA/v8BAAAA//8DAPz/AwD+/wIA/v8CAP3/AwD+/wEAAAD//wEA//8BAAAA//8BAAAA/v8EAPv/BAD+/wAAAAABAP7/AgD//wAAAAABAP7/AgD//wAAAAAAAAAAAAABAP3/BAD8/wQA/f8BAAAAAAABAP7/AgD+/wIA/v8CAP7/AgD+/wIA/v8BAAAAAAD//wIA/f8DAP7/AAABAP//AAACAPz/BAD9/wIA//8AAP//AwD9/wMA/P8EAP3/AwD9/wIA//8BAP//AQD+/wMA/f8DAP7/AAABAP//AQAAAP//AQD//wIA/f8DAP7/AQAAAP//AQAAAAAA//8CAP7/AQABAP7/AgD+/wEAAQD+/wIA/v8CAP////8CAP7/AgD//wAAAAABAP7/AwD9/wIAAAD+/wMA/f8CAP//AQD+/wMA/f8CAP//AAACAPz/BQD6/wUA/v///wIA/v8CAP3/BAD7/wYA+v8FAPz/AwD/////AgD+/wEAAAD//wEAAAD//wIA/f8DAP7/AQAAAP//AgD//wAA//8BAAAAAAAAAP//AQD//wEA//8AAAIA/f8DAP3/AgAAAP//AQD//wEA//8AAAEA//8BAP////8CAP//AAABAP3/BAD9/wIA/v8BAAEA//8BAP7/AgD//wEA//8AAAEA//8BAP//AAAAAAEA//8BAP7/AgD//wEA//8AAAAAAQD+/wIA/v8BAAAAAAD//wIA/v8BAAAAAAAAAAAAAQD+/wMA/f8CAP//AQD//wIA/f8DAP7/AQD//wEA//8CAP7/AAABAP7/AwD9/wMA/v8AAAEA//8BAAAAAAD//wIA/v8BAAAA//8CAP7/AgD+/wEA//8CAP7/AgD//wAAAAAAAAAAAQD//wEA/v8DAPz/BQD8/wIA//8AAAEAAAD//wEA//8BAP//AQAAAAAA//8BAP//AgD+/wEAAAAAAP//AQD+/wMA/////wEA/v8CAP//AQD//wEA//8AAAEA//8BAAAA/v8EAPz/AwD+/wEAAAAAAAAA//8CAP7/AQD//wEA//8BAP//AAABAP7/AwD9/wIA//8BAP//AQD//wEA//8AAAEA/v8EAPv/BAD9/wIA//8BAP7/AwD9/wIA//8AAAEA//8BAP//AQD//wAAAQD//wEAAAD+/wMA/v8AAAIA/f8DAP7/AQD//wAAAQD+/wMA/f8CAP//AAABAP7/AgD+/wMA/f8CAP7/AQABAP7/AgD+/wIA/v8CAP7/AwD8/wMA//8AAAEA//8AAAAAAAABAP//AQD//wAAAQD//wIA/f8DAP3/AwD+/wAAAgD9/wIA//8AAAEAAAD+/wMA/P8FAPv/BAD9/wIA//8AAP//AgD+/wIA/v8BAAAAAAD//wEAAAAAAP//AQD//wEA//8BAP//AAABAP7/AwD9/wIA//8BAP//AAABAP//AQD//wAAAQD//wEA//8BAP//AAABAAAA//8BAP7/AwD9/wMA/f8DAP3/AgD//wEA//8BAP7/AgD//wAAAgD8/wQA/f8CAP//AQD+/wMA/f8CAP7/AgD//wAAAAAAAAAAAAABAP7/AwD9/wIA/v8DAP3/AwD9/wIA/v8DAPz/BQD7/wQA/f8CAP7/AwD9/wMA/f8CAP//AQAAAP7/AwD+/wEA//8AAAEAAAAAAP//AAABAP//AQAAAP7/AwD9/wMA/f8CAP//AQD//wEA//8AAAIA/f8CAAAA//8BAAAA//8BAAAA/v8EAPv/BAD9/wIA//8AAAEA/v8CAP//AAABAP//AAABAP//AAABAP7/AwD8/wQA/f8CAAAA/v8DAP3/AwD9/wMA/v8BAAAA//8BAAAA//8CAP7/AQAAAAAAAAAAAAAA//8CAP7/AgD+/wIA/v8CAP7/AgD//wAAAQD//wAAAQD//wAAAQD//wAAAQD+/wIA//8AAAAAAQD+/wMA/f8CAP//AQD//wEA//8AAAEA/v8DAP3/AgD//wAAAAABAP7/AwD9/wIA//8AAAEA/v8DAP3/AgD//wAAAAABAP7/AwD8/wMA/v8CAP//AAD//wIA/v8CAP7/AQABAP7/AQAAAP//AgD/////AQD//wEAAAD//wEA/v8EAPv/BAD9/wMA/v8BAAAA//8BAAEA/P8GAPr/BQD8/wMA/v8BAAAA//8CAP7/AQABAP3/BAD7/wYA+/8EAPz/AwD//wEA//8BAP7/BAD8/wMA/v8AAAIA/v8BAAAA//8BAAAA//8BAAAA//8CAP3/AwD+/wAAAgD8/wUA/P8DAP7/AAABAAAAAAD//wEAAAD//wIA/f8DAP7/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA/f8EAPz/AwD/////AgD+/wIA/f8DAP7/AgD+/wEA//8CAP7/AQD//wEAAAAAAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAP//AQAAAP//AAACAP3/BAD7/wQA/v8BAAAA//8BAP//AQAAAP//AQAAAP7/BAD7/wUA+/8EAP3/AgD//wAAAQD+/wIA//8AAAEA/v8CAP//AQD+/wEAAAAAAAAAAAD//wEA//8CAP3/AwD9/wIA//8AAAAAAAAAAAAA//8BAP//AgD+/wEA//8CAP7/AQAAAP//AgD/////AgD/////AgD+/wIA//8AAP//AQABAP7/AgD9/wMA/v8CAP////8BAAAAAAAAAAAA//8CAP////8DAPz/AwD+/wEAAAAAAP//AQD//wEAAAD//wEAAAD+/wQA+/8FAPz/AgAAAP//AgD9/wMA/v8BAAAAAAD//wEAAAD//wIA/v8BAAAAAAD//wIA/v8BAAAA//8BAAAA//8CAP7/AQD//wEA//8BAAAA//8BAP//AAABAP//AQAAAP7/AgD//wEA//8AAAAAAQD+/wMA/P8EAP7///8DAPz/BQD8/wEAAQD+/wMA/v8AAAEA//8BAP//AQD//wEA/v8CAP//AQD//wAAAAABAAAA//8BAP//AQAAAAAA//8BAP//AgD+/wAAAQD//wIA/f8CAP//AQAAAP7/AwD9/wMA/v8BAP//AAABAP//AgD9/wIA//8BAAAA//8BAAAA//8CAP3/AwD+/wEAAAD+/wQA/P8DAP7/AAACAP7/AQAAAP//AQAAAP//AQAAAP//AgD9/wIAAAD//wIA/f8DAP7/AQD//wEA//8CAP7/AQD//wAAAQD//wEA//8AAAAAAQD//wEAAAD9/wUA+/8FAPz/AgD//wAAAQD//wAAAQD+/wMA/f8BAAEA/v8CAP7/AgD+/wIA/v8BAAAAAAAAAAAAAAD//wIA/v8CAP////8CAP7/AgD+/wIA/v8CAP7/AQAAAP//AQAAAP//AQD//wAAAQD//wAAAQD+/wMA/f8CAAAA/v8DAP3/AgAAAP//AQAAAP7/AwD9/wMA/v8BAP//AQD//wEAAAD+/wMA/f8CAAAA/v8CAP//AAAAAAEA//8AAAEA/v8DAP3/AwD9/wIA//8BAP//AgD8/wQA/v8BAAAA/v8CAP//AQD//wAAAAAAAAEA/f8EAPz/BAD9/wIA//8AAAAAAAABAP//AAAAAAAAAAABAP3/BAD9/wIA/v8BAAEA//8AAAAA//8CAP7/AgD9/wQA+/8FAPv/BQD8/wMA/f8DAP3/AwD+/wAAAgD9/wMA/f8CAAAA/v8EAPv/BQD7/wUA/P8DAP///v8DAP3/BAD8/wMA/f8DAP7/AQD//wEAAAD//wEA/v8CAAAA/v8CAP7/AgD//wAAAAAAAAAAAQD+/wIA//8AAAEA/v8DAPz/BAD9/wIA//8AAP//AgD//wEA/v8BAAAAAQD//wAAAAAAAAEA//8AAAEA//8BAP//AAABAP//AQD+/wIA/v8DAPz/BAD8/wQA/f8BAAAAAQD+/wMA/P8DAP//AAAAAAAAAAD//wMA+/8FAP3/AQABAP3/BAD8/wMA/v8BAAAA//8CAP3/AwD+/wEAAQD9/wMA/f8EAPz/BAD7/wQA/v8BAAEA/f8DAP7/AQAAAP//AgD+/wEAAAD//wIA/v8CAP7/AgD+/wEAAQD//wEA/v8CAP7/BAD7/wQA/f8CAAAA//8AAAAAAAABAP//AQD+/wEAAQD+/wMA/f8BAAEA/v8DAPz/AwD/////AwD8/wQA/P8DAP7/AgD//wAA//8BAAAAAAAAAP//AgD+/wEAAAD//wIA/v8BAAAA//8CAP3/AgD//wAAAQD+/wIA/v8BAAAA//8CAP7/AgD+/wEA//8CAP3/BAD7/wQA/v8BAAAA//8AAAEAAAD//wIA/f8DAP7/AgD+/wIA/v8CAP7/AgD+/wEAAAAAAP//AgD9/wMA/v8BAP//AgD9/wMA/v8AAAEA//8BAP//AQD//wEA//8AAAEA/v8EAPz/AgD//wAAAQAAAP//AAABAP//AQD//wEAAAD//wEA//8BAAEA/f8DAP7/AQABAP3/AwD+/wIA/////wEAAAAAAAAAAAD//wIA/v8CAP////8CAP7/AgD//wAA//8CAP3/BAD9/wAAAgD9/wMA/v8BAP//AQAAAP//AQAAAP//AgD9/wMA/f8EAPz/AwD+/wEAAAAAAAAAAAD//wIA/f8EAP3/AAABAAAA//8CAP7/AQAAAP//AQAAAAAA//8BAP//AQAAAP//AQAAAP//AQAAAP//AgD9/wMA/v8BAP//AQAAAP//AQD//wIA/v8CAP3/BAD9/wEAAAD//wEAAQD9/wMA/f8CAAAA/v8DAP3/AgD//wAAAQD+/wIA/v8CAP7/AQAAAP//AgD+/wEAAAAAAP//AwD7/wUA/f8BAAEA/v8BAAEA/v8DAP3/AgD//wEA//8BAP//AQD//wEA//8CAP3/BAD7/wQA/////wIA/v8AAAIA/v8CAP3/BAD7/wUA/P8DAP3/AwD9/wMA/v8AAAIA/v8CAP7/AgD+/wIA//8AAAEA/v8CAP7/AgD//wAAAAD//wEAAAAAAAAA//8BAP7/BAD7/wUA/P8CAAAA//8BAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAAAA//8CAP3/AwD+/wEA//8CAP3/AwD+/wAAAwD8/wIAAAD//wIA/////wIA/v8CAP7/AgD+/wEAAAAAAAAAAAAAAP//AgD+/wIA//8AAAAA//8CAP7/AgD+/wEA//8CAP3/AwD9/wMA/v8BAP7/AwD9/wMA/f8CAP//AQD+/wIA//8BAP//AQD+/wMA/v8BAAAA//8BAAAA//8CAP7/AQAAAP//AgD+/wIA/v8CAP//AAAAAAEA//8BAP//AAABAAAA//8BAP//AQD//wEA//8BAP//AQAAAP//AQD//wEAAAD//wIA/f8CAAAA//8BAAAA//8BAP//AAABAP//AQD//wAAAAAAAAEA/v8CAP//AQD//wAAAAABAP7/AwD9/wIAAAD+/wIA//8BAP//AgD9/wMA/f8DAP7/AgD+/wEAAAAAAAEA/v8CAP7/AgD//wAAAAAAAAAAAAAAAP//AgD/////AgD9/wQA/f8BAAAAAAAAAAEA/f8DAP////8DAP3/AQABAP7/AgD//wAAAQD+/wMA/f8CAP7/AQABAP7/AwD7/wYA+v8FAP3/AQABAP7/AgD+/wMA/f8CAP7/AwD+/wEA//8BAP//AQAAAP7/BQD5/wcA+v8FAPz/AwD+/wIA/v8BAAAA//8DAPv/BQD8/wMA/////wEAAAAAAAAAAAD//wIA/f8DAP7/AQAAAP//AQAAAP//AgD+/wIA/v8BAAEA/f8EAPz/AwD+/wEA//8CAP7/AQD//wEA//8CAP7/AQAAAP//AgD+/wEAAAAAAAAAAAAAAAAAAAD//wIA/f8EAPz/AwD+/wEA//8CAP7/AgD+/wEAAQD+/wEAAQD+/wIA/////wIA//8AAAAAAAAAAAAAAAD//wEAAAAAAP//AgD9/wMA/v8BAP//AQAAAP//AQD//wEA//8BAP//AQD//wEA//8BAP//AQAAAP7/AwD9/wMA/v8BAP7/AwD9/wMA/v8BAP//AAABAP//AQD//wAAAAABAP//AAAAAAAAAQD//wEA/v8CAAAA/v8EAPv/BAD9/wIAAAD+/wMA/P8DAP//AAAAAP//AQD//wIA/f8DAP3/AwD9/wMA/v8BAAAA//8BAAAA//8CAP3/AwD9/wQA+/8FAPv/BQD8/wMA/v8BAAAA//8BAP//AgD+/wEAAAD//wIA/v8BAAEA/f8DAP3/AgAAAP//AQD//wAAAQD//wEA//8BAP//AQD//wEA/v8DAP3/AgAAAP7/AwD9/wIAAAD//wEAAAD//wIA/f8DAP7/AgD9/wQA+/8FAPz/AgAAAP//AgD9/wIA//8BAP//AQD//wEA//8BAP//AQD//wIA/f8DAP3/AgD//wAAAQD+/wIA/v8BAAEA/v8CAP7/AgD+/wMA/P8DAP//AAABAP7/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA/v8CAP3/BAD8/wMA/v8BAAAAAAD//wEAAAAAAAAAAAD//wEAAAAAAAAA//8BAP//AgD+/wEA//8CAP3/AwD9/wMA/f8EAPv/BAD+/wAAAQD//wEA//8BAP//AAABAP//AQD//wEAAAD//wEA//8BAP//AgD9/wMA/v8AAAIA/f8DAP7/AAACAP3/AwD+/wEA//8BAP//AQAAAP//AQAAAP7/AwD9/wMA/v8AAAEA//8BAP//AAAAAAEA//8AAAEA/v8CAP//AAAAAAEA/v8DAPz/BAD9/wEAAQD+/wEAAQD9/wQA/P8DAP7/AQAAAAAAAAAAAAAAAAAAAAAAAQD+/wIA/////wIA/v8BAAAA//8BAP//AQD//wEA//8BAAAA/v8EAPz/AwD///7/BAD8/wMA/////wIA/v8CAP////8CAP7/AgD+/wIA/v8CAP////8CAP7/AwD9/wIA/v8CAP//AAABAP7/AwD9/wEAAQD+/wMA/f8CAP//AAAAAAEA/v8DAPz/BAD9/wIA/v8CAP7/AgD//wAAAAD//wIA/v8CAP7/AQAAAAAA//8CAP7/AgD+/wIA/v8CAP7/AwD8/wUA+v8GAPv/AwD//wAAAAAAAAAA//8DAPv/BQD9/wAAAgD9/wMA/v8BAP//AQAAAP//AgD9/wMA/v8BAAAA//8BAAAAAAAAAP//AQAAAAAAAAD//wEA//8CAP3/AwD+/wAAAgD+/wEAAAD//wIA/v8CAP7/AgD/////AwD8/wUA/P8CAP//AQD//wIA/f8DAP3/AwD+/wAAAQD+/wMA/f8DAP3/AgD//wAAAQD//wEA//8BAP7/AwD+/wEA//8AAAEA//8CAPz/BAD9/wIA//8AAAEA/v8DAPz/BAD9/wIA//8AAAEA/v8CAP7/AgD//wEA/f8EAPz/BAD+////AgD//wAAAQD//wAAAQD//wEA//8BAP7/AwD+/wEA';
+ }
+ } catch (o) {
+ return void addTest('audiopreload', !1);
+ }
+ (r.setAttribute('preload', 'auto'),
+ (r.style.cssText = 'display:none'),
+ docElement.appendChild(r),
+ setTimeout(function () {
+ (r.addEventListener('loadeddata', A, !1), (e = setTimeout(A, t)));
+ }, 0));
+ }),
+ Modernizr.addTest('canvasblending', function () {
+ if (Modernizr.canvas === !1) return !1;
+ var A = createElement('canvas').getContext('2d');
+ try {
+ A.globalCompositeOperation = 'screen';
+ } catch (e) {}
+ return 'screen' === A.globalCompositeOperation;
+ }));
+ var canvas = createElement('canvas');
+ (Modernizr.addTest('todataurljpeg', function () {
+ return (
+ !!Modernizr.canvas &&
+ 0 === canvas.toDataURL('image/jpeg').indexOf('data:image/jpeg')
+ );
+ }),
+ Modernizr.addTest('todataurlpng', function () {
+ return (
+ !!Modernizr.canvas &&
+ 0 === canvas.toDataURL('image/png').indexOf('data:image/png')
+ );
+ }),
+ Modernizr.addTest('todataurlwebp', function () {
+ var A = !1;
+ try {
+ A =
+ !!Modernizr.canvas &&
+ 0 === canvas.toDataURL('image/webp').indexOf('data:image/webp');
+ } catch (e) {}
+ return A;
+ }),
+ Modernizr.addTest('canvaswinding', function () {
+ if (Modernizr.canvas === !1) return !1;
+ var A = createElement('canvas').getContext('2d');
+ return (
+ A.rect(0, 0, 10, 10),
+ A.rect(2, 2, 6, 6),
+ A.isPointInPath(5, 5, 'evenodd') === !1
+ );
+ }),
+ Modernizr.addTest('bgpositionshorthand', function () {
+ var A = createElement('a'),
+ e = A.style,
+ t = 'right 10px bottom 10px';
+ return (
+ (e.cssText = 'background-position: ' + t + ';'),
+ e.backgroundPosition === t
+ );
+ }),
+ Modernizr.addTest('csscalc', function () {
+ var A = 'width:',
+ e = 'calc(10px);',
+ t = createElement('a');
+ return ((t.style.cssText = A + prefixes.join(e + A)), !!t.style.length);
+ }),
+ Modernizr.addTest('cubicbezierrange', function () {
+ var A = createElement('a');
+ return (
+ (A.style.cssText = prefixes.join(
+ 'transition-timing-function:cubic-bezier(1,0,0,1.1); '
+ )),
+ !!A.style.length
+ );
+ }),
+ Modernizr.addTest('cssgradients', function () {
+ for (
+ var A,
+ e = 'background-image:',
+ t = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
+ r = '',
+ n = 0,
+ o = prefixes.length - 1;
+ o > n;
+ n++
+ )
+ ((A = 0 === n ? 'to ' : ''),
+ (r +=
+ e +
+ prefixes[n] +
+ 'linear-gradient(' +
+ A +
+ 'left top, #9f9, white);'));
+ Modernizr._config.usePrefixes && (r += e + '-webkit-' + t);
+ var i = createElement('a'),
+ d = i.style;
+ return (
+ (d.cssText = r), ('' + d.backgroundImage).indexOf('gradient') > -1
+ );
+ }),
+ Modernizr.addTest('multiplebgs', function () {
+ var A = createElement('a').style;
+ return (
+ (A.cssText =
+ 'background:url(https://),url(https://),red url(https://)'),
+ /(url\s*\(.*?){3}/.test(A.background)
+ );
+ }),
+ Modernizr.addTest('opacity', function () {
+ var A = createElement('a').style;
+ return (
+ (A.cssText = prefixes.join('opacity:.55;')), /^0.55$/.test(A.opacity)
+ );
+ }),
+ Modernizr.addTest('csspointerevents', function () {
+ var A = createElement('a').style;
+ return ((A.cssText = 'pointer-events:auto'), 'auto' === A.pointerEvents);
+ }),
+ Modernizr.addTest('csspositionsticky', function () {
+ var A = 'position:',
+ e = 'sticky',
+ t = createElement('a'),
+ r = t.style;
+ return (
+ (r.cssText = A + prefixes.join(e + ';' + A).slice(0, -A.length)),
+ -1 !== r.position.indexOf(e)
+ );
+ }),
+ Modernizr.addTest('cssremunit', function () {
+ var A = createElement('a').style;
+ try {
+ A.fontSize = '3rem';
+ } catch (e) {}
+ return /rem/.test(A.fontSize);
+ }),
+ Modernizr.addTest('rgba', function () {
+ var A = createElement('a').style;
+ return (
+ (A.cssText = 'background-color:rgba(150,255,150,.5)'),
+ ('' + A.backgroundColor).indexOf('rgba') > -1
+ );
+ }),
+ Modernizr.addTest('preserve3d', function () {
+ var A,
+ e,
+ t = window.CSS,
+ r = !1;
+ return t && t.supports && t.supports('(transform-style: preserve-3d)')
+ ? !0
+ : ((A = createElement('a')),
+ (e = createElement('a')),
+ (A.style.cssText =
+ 'display: block; transform-style: preserve-3d; transform-origin: right; transform: rotateY(40deg);'),
+ (e.style.cssText =
+ 'display: block; width: 9px; height: 1px; background: #000; transform-origin: right; transform: rotateY(40deg);'),
+ A.appendChild(e),
+ docElement.appendChild(A),
+ (r = e.getBoundingClientRect()),
+ docElement.removeChild(A),
+ (r = r.width && r.width < 4));
+ }),
+ Modernizr.addTest('dataset', function () {
+ var A = createElement('div');
+ return (
+ A.setAttribute('data-a-b', 'c'), !(!A.dataset || 'c' !== A.dataset.aB)
+ );
+ }),
+ Modernizr.addTest('hidden', 'hidden' in createElement('a')),
+ Modernizr.addTest('outputelem', 'value' in createElement('output')),
+ Modernizr.addTest(
+ 'progressbar',
+ createElement('progress').max !== undefined
+ ),
+ Modernizr.addTest('meter', createElement('meter').max !== undefined),
+ Modernizr.addTest('ruby', function () {
+ function A(A, e) {
+ var t;
+ return (
+ window.getComputedStyle
+ ? (t = document.defaultView
+ .getComputedStyle(A, null)
+ .getPropertyValue(e))
+ : A.currentStyle && (t = A.currentStyle[e]),
+ t
+ );
+ }
+ function e() {
+ (docElement.removeChild(t), (t = null), (r = null), (n = null));
+ }
+ var t = createElement('ruby'),
+ r = createElement('rt'),
+ n = createElement('rp'),
+ o = 'display',
+ i = 'fontSize';
+ return (
+ t.appendChild(n),
+ t.appendChild(r),
+ docElement.appendChild(t),
+ 'none' == A(n, o) ||
+ ('ruby' == A(t, o) && 'ruby-text' == A(r, o)) ||
+ ('6pt' == A(n, i) && '6pt' == A(r, i))
+ ? (e(), !0)
+ : (e(), !1)
+ );
+ }),
+ Modernizr.addTest('template', 'content' in createElement('template')),
+ Modernizr.addTest('time', 'valueAsDate' in createElement('time')),
+ Modernizr.addTest(
+ 'texttrackapi',
+ 'function' == typeof createElement('video').addTextTrack
+ ),
+ Modernizr.addTest('track', 'kind' in createElement('track')),
+ Modernizr.addTest('unknownelements', function () {
+ var A = createElement('a');
+ return ((A.innerHTML = ' '), 1 === A.childNodes.length);
+ }),
+ Modernizr.addTest('capture', 'capture' in createElement('input')),
+ Modernizr.addTest('fileinput', function () {
+ if (
+ navigator.userAgent.match(
+ /(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/
+ )
+ )
+ return !1;
+ var A = createElement('input');
+ return ((A.type = 'file'), !A.disabled);
+ }),
+ Modernizr.addTest('fileinputdirectory', function () {
+ var A = createElement('input'),
+ e = 'directory';
+ if (((A.type = 'file'), e in A)) return !0;
+ for (var t = 0, r = domPrefixes.length; r > t; t++)
+ if (domPrefixes[t] + e in A) return !0;
+ return !1;
+ }),
+ Modernizr.addTest('formattribute', function () {
+ var A,
+ e = createElement('form'),
+ t = createElement('input'),
+ r = createElement('div'),
+ n = 'formtest' + new Date().getTime(),
+ o = !1;
+ e.id = n;
+ try {
+ t.setAttribute('form', n);
+ } catch (i) {
+ document.createAttribute &&
+ ((A = document.createAttribute('form')),
+ (A.nodeValue = n),
+ t.setAttributeNode(A));
+ }
+ return (
+ r.appendChild(e),
+ r.appendChild(t),
+ docElement.appendChild(r),
+ (o = e.elements && 1 === e.elements.length && t.form == e),
+ r.parentNode.removeChild(r),
+ o
+ );
+ }),
+ Modernizr.addTest(
+ 'placeholder',
+ 'placeholder' in createElement('input') &&
+ 'placeholder' in createElement('textarea')
+ ),
+ Modernizr.addTest('sandbox', 'sandbox' in createElement('iframe')),
+ Modernizr.addTest('seamless', 'seamless' in createElement('iframe')),
+ Modernizr.addTest('srcdoc', 'srcdoc' in createElement('iframe')),
+ Modernizr.addAsyncTest(function () {
+ if (!Modernizr.canvas) return !1;
+ var A = new Image(),
+ e = createElement('canvas'),
+ t = e.getContext('2d');
+ ((A.onload = function () {
+ addTest('apng', function () {
+ return 'undefined' == typeof e.getContext
+ ? !1
+ : (t.drawImage(A, 0, 0), 0 === t.getImageData(0, 0, 1, 1).data[3]);
+ });
+ }),
+ (A.src =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACGFjVEwAAAABAAAAAcMq2TYAAAANSURBVAiZY2BgYPgPAAEEAQB9ssjfAAAAGmZjVEwAAAAAAAAAAQAAAAEAAAAAAAAAAAD6A+gBAbNU+2sAAAARZmRBVAAAAAEImWNgYGBgAAAABQAB6MzFdgAAAABJRU5ErkJggg=='));
+ }),
+ Modernizr.addAsyncTest(function () {
+ var A,
+ e,
+ t,
+ r = createElement('img'),
+ n = 'sizes' in r;
+ !n && 'srcset' in r
+ ? ((e =
+ 'data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw=='),
+ (A =
+ 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='),
+ (t = function () {
+ addTest('sizes', 2 == r.width);
+ }),
+ (r.onload = t),
+ (r.onerror = t),
+ r.setAttribute('sizes', '9px'),
+ (r.srcset = A + ' 1w,' + e + ' 8w'),
+ (r.src = A))
+ : addTest('sizes', n);
+ }),
+ Modernizr.addTest('srcset', 'srcset' in createElement('img')),
+ Modernizr.addTest(
+ 'inputformaction',
+ !!('formAction' in createElement('input')),
+ { aliases: ['input-formaction'] }
+ ),
+ Modernizr.addTest(
+ 'inputformenctype',
+ !!('formEnctype' in createElement('input')),
+ { aliases: ['input-formenctype'] }
+ ),
+ Modernizr.addTest(
+ 'inputformmethod',
+ !!('formMethod' in createElement('input'))
+ ),
+ Modernizr.addTest(
+ 'inputformtarget',
+ !!('formtarget' in createElement('input')),
+ { aliases: ['input-formtarget'] }
+ ),
+ Modernizr.addTest('scriptasync', 'async' in createElement('script')),
+ Modernizr.addTest('scriptdefer', 'defer' in createElement('script')),
+ Modernizr.addTest('stylescoped', 'scoped' in createElement('style')),
+ Modernizr.addTest('inlinesvg', function () {
+ var A = createElement('div');
+ return (
+ (A.innerHTML = ''),
+ 'http://www.w3.org/2000/svg' ==
+ ('undefined' != typeof SVGRect &&
+ A.firstChild &&
+ A.firstChild.namespaceURI)
+ );
+ }),
+ Modernizr.addTest(
+ 'textareamaxlength',
+ !!('maxLength' in createElement('textarea'))
+ ),
+ Modernizr.addAsyncTest(function () {
+ function A(i) {
+ (n++, clearTimeout(e));
+ var d = (i && 'playing' === i.type) || 0 !== o.currentTime;
+ return !d && r > n
+ ? void (e = setTimeout(A, t))
+ : (o.removeEventListener('playing', A, !1),
+ addTest('videoautoplay', d),
+ void (o.parentNode && o.parentNode.removeChild(o)));
+ }
+ var e,
+ t = 200,
+ r = 5,
+ n = 0,
+ o = createElement('video'),
+ i = o.style;
+ if (!(Modernizr.video && 'autoplay' in o))
+ return void addTest('videoautoplay', !1);
+ ((i.position = 'absolute'), (i.height = 0), (i.width = 0));
+ try {
+ if (Modernizr.video.ogg)
+ o.src =
+ 'data:video/ogg;base64,T2dnUwACAAAAAAAAAABmnCATAAAAAHDEixYBKoB0aGVvcmEDAgEAAQABAAAQAAAQAAAAAAAFAAAAAQAAAAAAAAAAAGIAYE9nZ1MAAAAAAAAAAAAAZpwgEwEAAAACrA7TDlj///////////////+QgXRoZW9yYSsAAABYaXBoLk9yZyBsaWJ0aGVvcmEgMS4xIDIwMDkwODIyIChUaHVzbmVsZGEpAQAAABoAAABFTkNPREVSPWZmbXBlZzJ0aGVvcmEtMC4yOYJ0aGVvcmG+zSj3uc1rGLWpSUoQc5zmMYxSlKQhCDGMYhCEIQhAAAAAAAAAAAAAEW2uU2eSyPxWEvx4OVts5ir1aKtUKBMpJFoQ/nk5m41mUwl4slUpk4kkghkIfDwdjgajQYC8VioUCQRiIQh8PBwMhgLBQIg4FRba5TZ5LI/FYS/Hg5W2zmKvVoq1QoEykkWhD+eTmbjWZTCXiyVSmTiSSCGQh8PB2OBqNBgLxWKhQJBGIhCHw8HAyGAsFAiDgUCw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDAwPEhQUFQ0NDhESFRUUDg4PEhQVFRUOEBETFBUVFRARFBUVFRUVEhMUFRUVFRUUFRUVFRUVFRUVFRUVFRUVEAwLEBQZGxwNDQ4SFRwcGw4NEBQZHBwcDhATFhsdHRwRExkcHB4eHRQYGxwdHh4dGxwdHR4eHh4dHR0dHh4eHRALChAYKDM9DAwOExo6PDcODRAYKDlFOA4RFh0zV1A+EhYlOkRtZ00YIzdAUWhxXDFATldneXhlSFxfYnBkZ2MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEhIVGRoaGhoSFBYaGhoaGhUWGRoaGhoaGRoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhESFh8kJCQkEhQYIiQkJCQWGCEkJCQkJB8iJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQREhgvY2NjYxIVGkJjY2NjGBo4Y2NjY2MvQmNjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRISEhUXGBkbEhIVFxgZGxwSFRcYGRscHRUXGBkbHB0dFxgZGxwdHR0YGRscHR0dHhkbHB0dHR4eGxwdHR0eHh4REREUFxocIBERFBcaHCAiERQXGhwgIiUUFxocICIlJRcaHCAiJSUlGhwgIiUlJSkcICIlJSUpKiAiJSUlKSoqEBAQFBgcICgQEBQYHCAoMBAUGBwgKDBAFBgcICgwQEAYHCAoMEBAQBwgKDBAQEBgICgwQEBAYIAoMEBAQGCAgAfF5cdH1e3Ow/L66wGmYnfIUbwdUTe3LMRbqON8B+5RJEvcGxkvrVUjTMrsXYhAnIwe0dTJfOYbWrDYyqUrz7dw/JO4hpmV2LsQQvkUeGq1BsZLx+cu5iV0e0eScJ91VIQYrmqfdVSK7GgjOU0oPaPOu5IcDK1mNvnD+K8LwS87f8Jx2mHtHnUkTGAurWZlNQa74ZLSFH9oF6FPGxzLsjQO5Qe0edcpttd7BXBSqMCL4k/4tFrHIPuEQ7m1/uIWkbDMWVoDdOSuRQ9286kvVUlQjzOE6VrNguN4oRXYGkgcnih7t13/9kxvLYKQezwLTrO44sVmMPgMqORo1E0sm1/9SludkcWHwfJwTSybR4LeAz6ugWVgRaY8mV/9SluQmtHrzsBtRF/wPY+X0JuYTs+ltgrXAmlk10xQHmTu9VSIAk1+vcvU4ml2oNzrNhEtQ3CysNP8UeR35wqpKUBdGdZMSjX4WVi8nJpdpHnbhzEIdx7mwf6W1FKAiucMXrWUWVjyRf23chNtR9mIzDoT/6ZLYailAjhFlZuvPtSeZ+2oREubDoWmT3TguY+JHPdRVSLKxfKH3vgNqJ/9emeEYikGXDFNzaLjvTeGAL61mogOoeG3y6oU4rW55ydoj0lUTSR/mmRhPmF86uwIfzp3FtiufQCmppaHDlGE0r2iTzXIw3zBq5hvaTldjG4CPb9wdxAme0SyedVKczJ9AtYbgPOzYKJvZZImsN7ecrxWZg5dR6ZLj/j4qpWsIA+vYwE+Tca9ounMIsrXMB4Stiib2SPQtZv+FVIpfEbzv8ncZoLBXc3YBqTG1HsskTTotZOYTG+oVUjLk6zhP8bg4RhMUNtfZdO7FdpBuXzhJ5Fh8IKlJG7wtD9ik8rWOJxy6iQ3NwzBpQ219mlyv+FLicYs2iJGSE0u2txzed++D61ZWCiHD/cZdQVCqkO2gJpdpNaObhnDfAPrT89RxdWFZ5hO3MseBSIlANppdZNIV/Rwe5eLTDvkfWKzFnH+QJ7m9QWV1KdwnuIwTNtZdJMoXBf74OhRnh2t+OTGL+AVUnIkyYY+QG7g9itHXyF3OIygG2s2kud679ZWKqSFa9n3IHD6MeLv1lZ0XyduRhiDRtrNnKoyiFVLcBm0ba5Yy3fQkDh4XsFE34isVpOzpa9nR8iCpS4HoxG2rJpnRhf3YboVa1PcRouh5LIJv/uQcPNd095ickTaiGBnWLKVWRc0OnYTSyex/n2FofEPnDG8y3PztHrzOLK1xo6RAml2k9owKajOC0Wr4D5x+3nA0UEhK2m198wuBHF3zlWWVKWLN1CHzLClUfuoYBcx4b1llpeBKmbayaR58njtE9onD66lUcsg0Spm2snsb+8HaJRn4dYcLbCuBuYwziB8/5U1C1DOOz2gZjSZtrLJk6vrLF3hwY4Io9xuT/ruUFRSBkNtUzTOWhjh26irLEPx4jPZL3Fo3QrReoGTTM21xYTT9oFdhTUIvjqTkfkvt0bzgVUjq/hOYY8j60IaO/0AzRBtqkTS6R5ellZd5uKdzzhb8BFlDdAcrwkE0rbXTOPB+7Y0FlZO96qFL4Ykg21StJs8qIW7h16H5hGiv8V2Cflau7QVDepTAHa6Lgt6feiEvJDM21StJsmOH/hynURrKxvUpQ8BH0JF7BiyG2qZpnL/7AOU66gt+reLEXY8pVOCQvSsBtqZTNM8bk9ohRcwD18o/WVkbvrceVKRb9I59IEKysjBeTMmmbA21xu/6iHadLRxuIzkLpi8wZYmmbbWi32RVAUjruxWlJ//iFxE38FI9hNKOoCdhwf5fDe4xZ81lgREhK2m1j78vW1CqkuMu/AjBNK210kzRUX/B+69cMMUG5bYrIeZxVSEZISmkzbXOi9yxwIfPgdsov7R71xuJ7rFcACjG/9PzApqFq7wEgzNJm2suWESPuwrQvejj7cbnQxMkxpm21lUYJL0fKmogPPqywn7e3FvB/FCNxPJ85iVUkCE9/tLKx31G4CgNtWTTPFhMvlu8G4/TrgaZttTChljfNJGgOT2X6EqpETy2tYd9cCBI4lIXJ1/3uVUllZEJz4baqGF64yxaZ+zPLYwde8Uqn1oKANtUrSaTOPHkhvuQP3bBlEJ/LFe4pqQOHUI8T8q7AXx3fLVBgSCVpMba55YxN3rv8U1Dv51bAPSOLlZWebkL8vSMGI21lJmmeVxPRwFlZF1CpqCN8uLwymaZyjbXHCRytogPN3o/n74CNykfT+qqRv5AQlHcRxYrC5KvGmbbUwmZY/29BvF6C1/93x4WVglXDLFpmbapmF89HKTogRwqqSlGbu+oiAkcWFbklC6Zhf+NtTLFpn8oWz+HsNRVSgIxZWON+yVyJlE5tq/+GWLTMutYX9ekTySEQPLVNQQ3OfycwJBM0zNtZcse7CvcKI0V/zh16Dr9OSA21MpmmcrHC+6pTAPHPwoit3LHHqs7jhFNRD6W8+EBGoSEoaZttTCZljfduH/fFisn+dRBGAZYtMzbVMwvul/T/crK1NQh8gN0SRRa9cOux6clC0/mDLFpmbarmF8/e6CopeOLCNW6S/IUUg3jJIYiAcDoMcGeRbOvuTPjXR/tyo79LK3kqqkbxkkMRAOB0GODPItnX3Jnxro/25Ud+llbyVVSN4ySGIgHA6DHBnkWzr7kz410f7cqO/Syt5KqpFVJwn6gBEvBM0zNtZcpGOEPiysW8vvRd2R0f7gtjhqUvXL+gWVwHm4XJDBiMpmmZtrLfPwd/IugP5+fKVSysH1EXreFAcEhelGmbbUmZY4Xdo1vQWVnK19P4RuEnbf0gQnR+lDCZlivNM22t1ESmopPIgfT0duOfQrsjgG4tPxli0zJmF5trdL1JDUIUT1ZXSqQDeR4B8mX3TrRro/2McGeUvLtwo6jIEKMkCUXWsLyZROd9P/rFYNtXPBli0z398iVUlVKAjFlY437JXImUTm2r/4ZYtMy61hf16RPJIU9nZ1MABAwAAAAAAAAAZpwgEwIAAABhp658BScAAAAAAADnUFBQXIDGXLhwtttNHDhw5OcpQRMETBEwRPduylKVB0HRdF0A';
+ else {
+ if (!Modernizr.video.h264) return void addTest('videoautoplay', !1);
+ o.src =
+ 'data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAAs1tZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0OCByMjYwMSBhMGNkN2QzIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTEgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEwIHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAD2WIhAA3//728P4FNjuZQQAAAu5tb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAAZAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACGHRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAEAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAgAAAAIAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAGQAAAAAAAEAAAAAAZBtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAACgAAAAEAFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAE7bWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAA+3N0YmwAAACXc3RzZAAAAAAAAAABAAAAh2F2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAgACAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAxYXZjQwFkAAr/4QAYZ2QACqzZX4iIhAAAAwAEAAADAFA8SJZYAQAGaOvjyyLAAAAAGHN0dHMAAAAAAAAAAQAAAAEAAAQAAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAABRzdHN6AAAAAAAAAsUAAAABAAAAFHN0Y28AAAAAAAAAAQAAADAAAABidWR0YQAAAFptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAAC1pbHN0AAAAJal0b28AAAAdZGF0YQAAAAEAAAAATGF2ZjU2LjQwLjEwMQ==';
+ }
+ } catch (d) {
+ return void addTest('videoautoplay', !1);
+ }
+ (o.setAttribute('autoplay', ''),
+ (o.style.cssText = 'display:none'),
+ docElement.appendChild(o),
+ setTimeout(function () {
+ (o.addEventListener('playing', A, !1), (e = setTimeout(A, t)));
+ }, 0));
+ }),
+ Modernizr.addTest('videoloop', 'loop' in createElement('video')),
+ Modernizr.addTest('videopreload', 'preload' in createElement('video')),
+ Modernizr.addAsyncTest(function () {
+ if (((Modernizr.webglextensions = !1), Modernizr.webgl)) {
+ var A, e, t;
+ try {
+ ((A = createElement('canvas')),
+ (e = A.getContext('webgl') || A.getContext('experimental-webgl')),
+ (t = e.getSupportedExtensions()));
+ } catch (r) {
+ return;
+ }
+ e !== undefined && (Modernizr.webglextensions = new Boolean(!0));
+ for (var n = -1, o = t.length; ++n < o;)
+ Modernizr.webglextensions[t[n]] = !0;
+ A = undefined;
+ }
+ }),
+ Modernizr.addAsyncTest(function () {
+ var A,
+ e,
+ t = function (A) {
+ docElement.contains(A) || docElement.appendChild(A);
+ },
+ r = function (A) {
+ A.fake && A.parentNode && A.parentNode.removeChild(A);
+ },
+ n = function (A, e) {
+ var t = !!A;
+ if (
+ (t && ((t = new Boolean(t)), (t.blocked = 'blocked' === A)),
+ addTest('flash', function () {
+ return t;
+ }),
+ e && s.contains(e))
+ ) {
+ for (; e.parentNode !== s;) e = e.parentNode;
+ s.removeChild(e);
+ }
+ };
+ try {
+ e =
+ 'ActiveXObject' in window &&
+ 'Pan' in new window.ActiveXObject('ShockwaveFlash.ShockwaveFlash');
+ } catch (o) {}
+ if (
+ ((A = !(
+ ('plugins' in navigator && 'Shockwave Flash' in navigator.plugins) ||
+ e
+ )),
+ A || isSVG)
+ )
+ n(!1);
+ else {
+ var i,
+ d,
+ a = createElement('embed'),
+ s = getBody();
+ if (
+ ((a.type = 'application/x-shockwave-flash'),
+ s.appendChild(a),
+ !('Pan' in a || e))
+ )
+ return (t(s), n('blocked', a), void r(s));
+ ((i = function () {
+ return (
+ t(s),
+ docElement.contains(s)
+ ? (docElement.contains(a)
+ ? ((d = a.style.cssText),
+ '' !== d ? n('blocked', a) : n(!0, a))
+ : n('blocked'),
+ void r(s))
+ : ((s = document.body || s),
+ (a = createElement('embed')),
+ (a.type = 'application/x-shockwave-flash'),
+ s.appendChild(a),
+ setTimeout(i, 1e3))
+ );
+ }),
+ setTimeout(i, 10));
+ }
+ }));
+ var mq = (function () {
+ var A = window.matchMedia || window.msMatchMedia;
+ return A
+ ? function (e) {
+ var t = A(e);
+ return (t && t.matches) || !1;
+ }
+ : function (A) {
+ var e = !1;
+ return (
+ injectElementWithStyles(
+ '@media ' + A + ' { #modernizr { position: absolute; } }',
+ function (A) {
+ e =
+ 'absolute' ==
+ (window.getComputedStyle
+ ? window.getComputedStyle(A, null)
+ : A.currentStyle
+ ).position;
+ }
+ ),
+ e
+ );
+ };
+ })();
+ ((ModernizrProto.mq = mq), Modernizr.addTest('mediaqueries', mq('only all')));
+ var testStyles = (ModernizrProto.testStyles = injectElementWithStyles);
+ (Modernizr.addTest('hiddenscroll', function () {
+ return testStyles(
+ '#modernizr {width:100px;height:100px;overflow:scroll}',
+ function (A) {
+ return A.offsetWidth === A.clientWidth;
+ }
+ );
+ }),
+ Modernizr.addTest('mathml', function () {
+ var A;
+ return (
+ testStyles(
+ '#modernizr{position:absolute;display:inline-block}',
+ function (e) {
+ ((e.innerHTML +=
+ ''),
+ (A = e.offsetHeight > e.offsetWidth));
+ }
+ ),
+ A
+ );
+ }),
+ Modernizr.addTest('touchevents', function () {
+ var A;
+ if (
+ 'ontouchstart' in window ||
+ (window.DocumentTouch && document instanceof DocumentTouch)
+ )
+ A = !0;
+ else {
+ var e = [
+ '@media (',
+ prefixes.join('touch-enabled),('),
+ 'heartz',
+ ')',
+ '{#modernizr{top:9px;position:absolute}}'
+ ].join('');
+ testStyles(e, function (e) {
+ A = 9 === e.offsetTop;
+ });
+ }
+ return A;
+ }),
+ Modernizr.addTest('unicoderange', function () {
+ return Modernizr.testStyles(
+ '@font-face{font-family:"unicodeRange";src:local("Arial");unicode-range:U+0020,U+002E}#modernizr span{font-size:20px;display:inline-block;font-family:"unicodeRange",monospace}#modernizr .mono{font-family:monospace}',
+ function (A) {
+ for (var e = ['.', '.', 'm', 'm'], t = 0; t < e.length; t++) {
+ var r = createElement('span');
+ ((r.innerHTML = e[t]),
+ (r.className = t % 2 ? 'mono' : ''),
+ A.appendChild(r),
+ (e[t] = r.clientWidth));
+ }
+ return e[0] !== e[1] && e[2] === e[3];
+ }
+ );
+ }),
+ Modernizr.addTest('unicode', function () {
+ var A,
+ e = createElement('span'),
+ t = createElement('span');
+ return (
+ testStyles(
+ '#modernizr{font-family:Arial,sans;font-size:300em;}',
+ function (r) {
+ ((e.innerHTML = isSVG ? '妇' : 'ᝣ'),
+ (t.innerHTML = isSVG ? '☆' : '☆'),
+ r.appendChild(e),
+ r.appendChild(t),
+ (A = 'offsetWidth' in e && e.offsetWidth !== t.offsetWidth));
+ }
+ ),
+ A
+ );
+ }),
+ Modernizr.addTest('checked', function () {
+ return testStyles(
+ '#modernizr {position:absolute} #modernizr input {margin-left:10px} #modernizr :checked {margin-left:20px;display:block}',
+ function (A) {
+ var e = createElement('input');
+ return (
+ e.setAttribute('type', 'checkbox'),
+ e.setAttribute('checked', 'checked'),
+ A.appendChild(e),
+ 20 === e.offsetLeft
+ );
+ }
+ );
+ }),
+ testStyles(
+ '#modernizr{display: table; direction: ltr}#modernizr div{display: table-cell; padding: 10px}',
+ function (A) {
+ var e,
+ t = A.childNodes;
+ ((e = t[0].offsetLeft < t[1].offsetLeft),
+ Modernizr.addTest('displaytable', e, { aliases: ['display-table'] }));
+ },
+ 2
+ ));
+ var blacklist = (function () {
+ var A = navigator.userAgent,
+ e = A.match(/w(eb)?osbrowser/gi),
+ t =
+ A.match(/windows phone/gi) &&
+ A.match(/iemobile\/([0-9])+/gi) &&
+ parseFloat(RegExp.$1) >= 9;
+ return e || t;
+ })();
+ (blacklist
+ ? Modernizr.addTest('fontface', !1)
+ : testStyles(
+ '@font-face {font-family:"font";src:url("https://")}',
+ function (A, e) {
+ var t = document.getElementById('smodernizr'),
+ r = t.sheet || t.styleSheet,
+ n = r
+ ? r.cssRules && r.cssRules[0]
+ ? r.cssRules[0].cssText
+ : r.cssText || ''
+ : '',
+ o = /src/i.test(n) && 0 === n.indexOf(e.split(' ')[0]);
+ Modernizr.addTest('fontface', o);
+ }
+ ),
+ testStyles(
+ '#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}',
+ function (A) {
+ Modernizr.addTest('generatedcontent', A.offsetHeight >= 6);
+ }
+ ),
+ Modernizr.addTest('cssinvalid', function () {
+ return testStyles(
+ '#modernizr input{height:0;border:0;padding:0;margin:0;width:10px} #modernizr input:invalid{width:50px}',
+ function (A) {
+ var e = createElement('input');
+ return ((e.required = !0), A.appendChild(e), e.clientWidth > 10);
+ }
+ );
+ }),
+ testStyles(
+ '#modernizr div {width:100px} #modernizr :last-child{width:200px;display:block}',
+ function (A) {
+ Modernizr.addTest(
+ 'lastchild',
+ A.lastChild.offsetWidth > A.firstChild.offsetWidth
+ );
+ },
+ 2
+ ),
+ testStyles(
+ '#modernizr div {width:1px} #modernizr div:nth-child(2n) {width:2px;}',
+ function (A) {
+ for (var e = A.getElementsByTagName('div'), t = !0, r = 0; 5 > r; r++)
+ t = t && e[r].offsetWidth === (r % 2) + 1;
+ Modernizr.addTest('nthchild', t);
+ },
+ 5
+ ),
+ testStyles(
+ '#modernizr{overflow: scroll; width: 40px; height: 40px; }#' +
+ prefixes
+ .join('scrollbar{width:10px} #modernizr::')
+ .split('#')
+ .slice(1)
+ .join('#') +
+ 'scrollbar{width:10px}',
+ function (A) {
+ Modernizr.addTest(
+ 'cssscrollbar',
+ 'scrollWidth' in A && 30 == A.scrollWidth
+ );
+ }
+ ),
+ Modernizr.addTest('siblinggeneral', function () {
+ return testStyles(
+ '#modernizr div {width:100px} #modernizr div ~ div {width:200px;display:block}',
+ function (A) {
+ return 200 == A.lastChild.offsetWidth;
+ },
+ 2
+ );
+ }),
+ testStyles(
+ '#modernizr{position: absolute; top: -10em; visibility:hidden; font: normal 10px arial;}#subpixel{float: left; font-size: 33.3333%;}',
+ function (A) {
+ var e = A.firstChild;
+ ((e.innerHTML = 'This is a text written in Arial'),
+ Modernizr.addTest(
+ 'subpixelfont',
+ window.getComputedStyle
+ ? '44px' !==
+ window.getComputedStyle(e, null).getPropertyValue('width')
+ : !1
+ ));
+ },
+ 1,
+ ['subpixel']
+ ),
+ Modernizr.addTest('cssvalid', function () {
+ return testStyles(
+ '#modernizr input{height:0;border:0;padding:0;margin:0;width:10px} #modernizr input:valid{width:50px}',
+ function (A) {
+ var e = createElement('input');
+ return (A.appendChild(e), e.clientWidth > 10);
+ }
+ );
+ }),
+ Modernizr.addTest('details', function () {
+ var A,
+ e = createElement('details');
+ return 'open' in e
+ ? (testStyles('#modernizr details{display:block}', function (t) {
+ (t.appendChild(e),
+ (e.innerHTML = 'a b'),
+ (A = e.offsetHeight),
+ (e.open = !0),
+ (A = A != e.offsetHeight));
+ }),
+ A)
+ : !1;
+ }),
+ Modernizr.addTest('oninput', function () {
+ var A,
+ e = createElement('input');
+ if (
+ (e.setAttribute('oninput', 'return'),
+ hasEvent('oninput', docElement) || 'function' == typeof e.oninput)
+ )
+ return !0;
+ try {
+ var t = document.createEvent('KeyboardEvent');
+ A = !1;
+ var r = function (e) {
+ ((A = !0), e.preventDefault(), e.stopPropagation());
+ };
+ (t.initKeyEvent(
+ 'keypress',
+ !0,
+ !0,
+ window,
+ !1,
+ !1,
+ !1,
+ !1,
+ 0,
+ 'e'.charCodeAt(0)
+ ),
+ docElement.appendChild(e),
+ e.addEventListener('input', r, !1),
+ e.focus(),
+ e.dispatchEvent(t),
+ e.removeEventListener('input', r, !1),
+ docElement.removeChild(e));
+ } catch (n) {
+ A = !1;
+ }
+ return A;
+ }),
+ Modernizr.addTest('formvalidation', function () {
+ var A = createElement('form');
+ if (!('checkValidity' in A && 'addEventListener' in A)) return !1;
+ if ('reportValidity' in A) return !0;
+ var e,
+ t = !1;
+ return (
+ (Modernizr.formvalidationapi = !0),
+ A.addEventListener(
+ 'submit',
+ function (A) {
+ ((!window.opera || window.operamini) && A.preventDefault(),
+ A.stopPropagation());
+ },
+ !1
+ ),
+ (A.innerHTML =
+ ''),
+ testStyles(
+ '#modernizr form{position:absolute;top:-99999em}',
+ function (r) {
+ (r.appendChild(A),
+ (e = A.getElementsByTagName('input')[0]),
+ e.addEventListener(
+ 'invalid',
+ function (A) {
+ ((t = !0), A.preventDefault(), A.stopPropagation());
+ },
+ !1
+ ),
+ (Modernizr.formvalidationmessage = !!e.validationMessage),
+ A.getElementsByTagName('button')[0].click());
+ }
+ ),
+ t
+ );
+ }));
+ var inputElem = createElement('input'),
+ inputattrs =
+ 'autocomplete autofocus list placeholder max min multiple pattern required step'.split(
+ ' '
+ ),
+ attrs = {};
+ ((Modernizr.input = (function (A) {
+ for (var e = 0, t = A.length; t > e; e++)
+ attrs[A[e]] = !!(A[e] in inputElem);
+ return (
+ attrs.list &&
+ (attrs.list = !(
+ !createElement('datalist') || !window.HTMLDataListElement
+ )),
+ attrs
+ );
+ })(inputattrs)),
+ Modernizr.addTest('datalistelem', Modernizr.input.list));
+ var inputtypes =
+ 'search tel url email datetime date month week time datetime-local number range color'.split(
+ ' '
+ ),
+ inputs = {};
+ ((Modernizr.inputtypes = (function (A) {
+ for (var e, t, r, n = A.length, o = '1)', i = 0; n > i; i++)
+ (inputElem.setAttribute('type', (e = A[i])),
+ (r = 'text' !== inputElem.type && 'style' in inputElem),
+ r &&
+ ((inputElem.value = o),
+ (inputElem.style.cssText = 'position:absolute;visibility:hidden;'),
+ /^range$/.test(e) && inputElem.style.WebkitAppearance !== undefined
+ ? (docElement.appendChild(inputElem),
+ (t = document.defaultView),
+ (r =
+ t.getComputedStyle &&
+ 'textfield' !==
+ t.getComputedStyle(inputElem, null).WebkitAppearance &&
+ 0 !== inputElem.offsetHeight),
+ docElement.removeChild(inputElem))
+ : /^(search|tel)$/.test(e) ||
+ (r = /^(url|email)$/.test(e)
+ ? inputElem.checkValidity && inputElem.checkValidity() === !1
+ : inputElem.value != o)),
+ (inputs[A[i]] = !!r));
+ return inputs;
+ })(inputtypes)),
+ Modernizr.addTest('localizednumber', function () {
+ if (!Modernizr.inputtypes.number) return !1;
+ if (!Modernizr.formvalidation) return !1;
+ var A,
+ e = createElement('div'),
+ t = getBody(),
+ r = (function () {
+ return docElement.insertBefore(
+ t,
+ docElement.firstElementChild || docElement.firstChild
+ );
+ })();
+ e.innerHTML = '';
+ var n = e.childNodes[0];
+ (r.appendChild(e), n.focus());
+ try {
+ (document.execCommand('SelectAll', !1),
+ document.execCommand('InsertText', !1, '1,1'));
+ } catch (o) {}
+ return (
+ (A =
+ 'number' === n.type && 1.1 === n.valueAsNumber && n.checkValidity()),
+ r.removeChild(e),
+ t.fake && r.parentNode.removeChild(r),
+ A
+ );
+ }));
+ var modElem = { elem: createElement('modernizr') };
+ (Modernizr._q.push(function () {
+ delete modElem.elem;
+ }),
+ Modernizr.addTest('csschunit', function () {
+ var A,
+ e = modElem.elem.style;
+ try {
+ ((e.fontSize = '3ch'), (A = -1 !== e.fontSize.indexOf('ch')));
+ } catch (t) {
+ A = !1;
+ }
+ return A;
+ }),
+ Modernizr.addTest('cssexunit', function () {
+ var A,
+ e = modElem.elem.style;
+ try {
+ ((e.fontSize = '3ex'), (A = -1 !== e.fontSize.indexOf('ex')));
+ } catch (t) {
+ A = !1;
+ }
+ return A;
+ }),
+ Modernizr.addTest('hsla', function () {
+ var A = createElement('a').style;
+ return (
+ (A.cssText = 'background-color:hsla(120,40%,100%,.5)'),
+ contains(A.backgroundColor, 'rgba') ||
+ contains(A.backgroundColor, 'hsla')
+ );
+ }),
+ testStyles('#modernizr { height: 50vh; }', function (A) {
+ var e = parseInt(window.innerHeight / 2, 10),
+ t = parseInt(computedStyle(A, null, 'height'), 10);
+ Modernizr.addTest('cssvhunit', t == e);
+ }),
+ testStyles('#modernizr { width: 50vw; }', function (A) {
+ var e = parseInt(window.innerWidth / 2, 10),
+ t = parseInt(computedStyle(A, null, 'width'), 10);
+ Modernizr.addTest('cssvwunit', t == e);
+ }),
+ Modernizr.addTest('bdi', function () {
+ var A = createElement('div'),
+ e = createElement('bdi');
+ ((e.innerHTML = 'إ'), A.appendChild(e), docElement.appendChild(A));
+ var t = 'rtl' === computedStyle(e, null, 'direction');
+ return (docElement.removeChild(A), t);
+ }),
+ testStyles(
+ '#modernizr1{width: 50vmax}#modernizr2{width:50px;height:50px;overflow:scroll}#modernizr3{position:fixed;top:0;left:0;bottom:0;right:0}',
+ function (A) {
+ var e = A.childNodes[2],
+ t = A.childNodes[1],
+ r = A.childNodes[0],
+ n = parseInt((t.offsetWidth - t.clientWidth) / 2, 10),
+ o = r.clientWidth / 100,
+ i = r.clientHeight / 100,
+ d = parseInt(50 * Math.max(o, i), 10),
+ a = parseInt(computedStyle(e, null, 'width'), 10);
+ Modernizr.addTest(
+ 'cssvmaxunit',
+ roundedEquals(d, a) || roundedEquals(d, a - n)
+ );
+ },
+ 3
+ ),
+ testStyles(
+ '#modernizr1{width: 50vm;width:50vmin}#modernizr2{width:50px;height:50px;overflow:scroll}#modernizr3{position:fixed;top:0;left:0;bottom:0;right:0}',
+ function (A) {
+ var e = A.childNodes[2],
+ t = A.childNodes[1],
+ r = A.childNodes[0],
+ n = parseInt((t.offsetWidth - t.clientWidth) / 2, 10),
+ o = r.clientWidth / 100,
+ i = r.clientHeight / 100,
+ d = parseInt(50 * Math.min(o, i), 10),
+ a = parseInt(computedStyle(e, null, 'width'), 10);
+ Modernizr.addTest(
+ 'cssvminunit',
+ roundedEquals(d, a) || roundedEquals(d, a - n)
+ );
+ },
+ 3
+ ));
+ var testXhrType = function (A) {
+ if ('undefined' == typeof XMLHttpRequest) return !1;
+ var e = new XMLHttpRequest();
+ e.open('get', '/', !0);
+ try {
+ e.responseType = A;
+ } catch (t) {
+ return !1;
+ }
+ return 'response' in e && e.responseType == A;
+ };
+ (Modernizr.addTest('xhrresponsetypearraybuffer', testXhrType('arraybuffer')),
+ Modernizr.addTest('xhrresponsetypedocument', testXhrType('document')),
+ Modernizr.addTest('xhrresponsetypeblob', testXhrType('blob')),
+ Modernizr.addTest('xhrresponsetypejson', testXhrType('json')),
+ Modernizr.addTest('xhrresponsetypetext', testXhrType('text')));
+ var toStringFn = {}.toString;
+ (Modernizr.addTest('svgclippaths', function () {
+ return (
+ !!document.createElementNS &&
+ /SVGClipPath/.test(
+ toStringFn.call(
+ document.createElementNS('http://www.w3.org/2000/svg', 'clipPath')
+ )
+ )
+ );
+ }),
+ Modernizr.addTest('svgforeignobject', function () {
+ return (
+ !!document.createElementNS &&
+ /SVGForeignObject/.test(
+ toStringFn.call(
+ document.createElementNS(
+ 'http://www.w3.org/2000/svg',
+ 'foreignObject'
+ )
+ )
+ )
+ );
+ }),
+ Modernizr.addTest('smil', function () {
+ return (
+ !!document.createElementNS &&
+ /SVGAnimate/.test(
+ toStringFn.call(
+ document.createElementNS('http://www.w3.org/2000/svg', 'animate')
+ )
+ )
+ );
+ }));
+ var mStyle = { style: modElem.elem.style };
+ Modernizr._q.unshift(function () {
+ delete mStyle.style;
+ });
+ var testProp = (ModernizrProto.testProp = function (A, e, t) {
+ return testProps([A], undefined, e, t);
+ });
+ (Modernizr.addTest('textshadow', testProp('textShadow', '1px 1px')),
+ (ModernizrProto.testAllProps = testPropsAll));
+ var prefixed = (ModernizrProto.prefixed = function (A, e, t) {
+ return 0 === A.indexOf('@')
+ ? atRule(A)
+ : (-1 != A.indexOf('-') && (A = cssToDOM(A)),
+ e ? testPropsAll(A, e, t) : testPropsAll(A, 'pfx'));
+ }),
+ prefixedCSS = (ModernizrProto.prefixedCSS = function (A) {
+ var e = prefixed(A);
+ return e && domToCSS(e);
+ });
+ Modernizr.addTest('batteryapi', !!prefixed('battery', navigator), {
+ aliases: ['battery-api']
+ });
+ var crypto = prefixed('crypto', window);
+ (Modernizr.addTest('crypto', !!prefixed('subtle', crypto)),
+ Modernizr.addTest('dart', !!prefixed('startDart', navigator)),
+ Modernizr.addTest(
+ 'fullscreen',
+ !(
+ !prefixed('exitFullscreen', document, !1) &&
+ !prefixed('cancelFullScreen', document, !1)
+ )
+ ),
+ Modernizr.addTest('gamepads', !!prefixed('getGamepads', navigator)),
+ Modernizr.addAsyncTest(function () {
+ var A;
+ try {
+ A = prefixed('indexedDB', window);
+ } catch (e) {}
+ if (A) {
+ var t = 'modernizr-' + Math.random(),
+ r = A.open(t);
+ ((r.onerror = function () {
+ r.error && 'InvalidStateError' === r.error.name
+ ? addTest('indexeddb', !1)
+ : (addTest('indexeddb', !0), detectDeleteDatabase(A, t));
+ }),
+ (r.onsuccess = function () {
+ (addTest('indexeddb', !0), detectDeleteDatabase(A, t));
+ }));
+ } else addTest('indexeddb', !1);
+ }),
+ Modernizr.addAsyncTest(function () {
+ var A,
+ e,
+ t,
+ r,
+ n = 'detect-blob-support',
+ o = !1;
+ try {
+ A = prefixed('indexedDB', window);
+ } catch (i) {}
+ if (!Modernizr.indexeddb || !Modernizr.indexeddb.deletedatabase)
+ return !1;
+ try {
+ A.deleteDatabase(n).onsuccess = function () {
+ ((e = A.open(n, 1)),
+ (e.onupgradeneeded = function () {
+ e.result.createObjectStore('store');
+ }),
+ (e.onsuccess = function () {
+ t = e.result;
+ try {
+ ((r = t
+ .transaction('store', 'readwrite')
+ .objectStore('store')
+ .put(new Blob(), 'key')),
+ (r.onsuccess = function () {
+ o = !0;
+ }),
+ (r.onerror = function () {
+ o = !1;
+ }));
+ } catch (i) {
+ o = !1;
+ } finally {
+ (addTest('indexeddbblob', o), t.close(), A.deleteDatabase(n));
+ }
+ }));
+ };
+ } catch (i) {
+ addTest('indexeddbblob', !1);
+ }
+ }),
+ Modernizr.addTest('intl', !!prefixed('Intl', window)),
+ Modernizr.addTest('pagevisibility', !!prefixed('hidden', document, !1)),
+ Modernizr.addTest('performance', !!prefixed('performance', window)),
+ Modernizr.addTest('pointerlock', !!prefixed('exitPointerLock', document)),
+ Modernizr.addTest('quotamanagement', function () {
+ var A = prefixed('temporaryStorage', navigator),
+ e = prefixed('persistentStorage', navigator);
+ return !(!A || !e);
+ }),
+ Modernizr.addTest(
+ 'requestanimationframe',
+ !!prefixed('requestAnimationFrame', window),
+ { aliases: ['raf'] }
+ ),
+ Modernizr.addTest('vibrate', !!prefixed('vibrate', navigator)),
+ Modernizr.addTest('webintents', !!prefixed('startActivity', navigator)),
+ Modernizr.addTest('lowbattery', function () {
+ var A = 0.2,
+ e = prefixed('battery', navigator);
+ return !!(e && !e.charging && e.level <= A);
+ }));
+ var crypto = prefixed('crypto', window),
+ supportsGetRandomValues;
+ if (crypto && 'getRandomValues' in crypto && 'Uint32Array' in window) {
+ var array = new Uint32Array(10),
+ values = crypto.getRandomValues(array);
+ supportsGetRandomValues = values && is(values[0], 'number');
+ }
+ (Modernizr.addTest('getrandomvalues', !!supportsGetRandomValues),
+ Modernizr.addTest(
+ 'backgroundblendmode',
+ prefixed('backgroundBlendMode', 'text')
+ ),
+ Modernizr.addTest('objectfit', !!prefixed('objectFit'), {
+ aliases: ['object-fit']
+ }),
+ Modernizr.addTest('regions', function () {
+ if (isSVG) return !1;
+ var A = prefixed('flowFrom'),
+ e = prefixed('flowInto'),
+ t = !1;
+ if (!A || !e) return t;
+ var r = createElement('iframe'),
+ n = createElement('div'),
+ o = createElement('div'),
+ i = createElement('div'),
+ d = 'modernizr_flow_for_regions_check';
+ ((o.innerText = 'M'),
+ (n.style.cssText = 'top: 150px; left: 150px; padding: 0px;'),
+ (i.style.cssText = 'width: 50px; height: 50px; padding: 42px;'),
+ (i.style[A] = d),
+ n.appendChild(o),
+ n.appendChild(i),
+ docElement.appendChild(n));
+ var a,
+ s,
+ l = o.getBoundingClientRect();
+ return (
+ (o.style[e] = d),
+ (a = o.getBoundingClientRect()),
+ (s = parseInt(a.left - l.left, 10)),
+ docElement.removeChild(n),
+ 42 == s
+ ? (t = !0)
+ : (docElement.appendChild(r),
+ (l = r.getBoundingClientRect()),
+ (r.style[e] = d),
+ (a = r.getBoundingClientRect()),
+ l.height > 0 &&
+ l.height !== a.height &&
+ 0 === a.height &&
+ (t = !0)),
+ (o = i = n = r = undefined),
+ t
+ );
+ }),
+ Modernizr.addTest('wrapflow', function () {
+ var A = prefixed('wrapFlow');
+ if (!A || isSVG) return !1;
+ var e = A.replace(/([A-Z])/g, function (A, e) {
+ return '-' + e.toLowerCase();
+ }).replace(/^ms-/, '-ms-'),
+ t = createElement('div'),
+ r = createElement('div'),
+ n = createElement('span');
+ ((r.style.cssText =
+ 'position: absolute; left: 50px; width: 100px; height: 20px;' +
+ e +
+ ':end;'),
+ (n.innerText = 'X'),
+ t.appendChild(r),
+ t.appendChild(n),
+ docElement.appendChild(t));
+ var o = n.offsetLeft;
+ return (docElement.removeChild(t), (r = n = t = undefined), 150 == o);
+ }),
+ Modernizr.addTest('filesystem', !!prefixed('requestFileSystem', window)),
+ Modernizr.addTest(
+ 'requestautocomplete',
+ !!prefixed('requestAutocomplete', createElement('form'))
+ ),
+ Modernizr.addTest(
+ 'speechrecognition',
+ !!prefixed('SpeechRecognition', window)
+ ));
+ var url = prefixed('URL', window, !1);
+ ((url = url && window[url]),
+ Modernizr.addTest(
+ 'bloburls',
+ url && 'revokeObjectURL' in url && 'createObjectURL' in url
+ ),
+ Modernizr.addAsyncTest(function () {
+ function A() {
+ (addTest('transferables', !1), e());
+ }
+ function e() {
+ (d && URL.revokeObjectURL(d), a && a.terminate(), n && clearTimeout(n));
+ }
+ var t = !!(
+ Modernizr.blobconstructor &&
+ Modernizr.bloburls &&
+ Modernizr.webworkers &&
+ Modernizr.typedarrays
+ );
+ if (!t) return addTest('transferables', !1);
+ try {
+ var r,
+ n,
+ o = 'var hello = "world"',
+ i = new Blob([o], { type: 'text/javascript' }),
+ d = URL.createObjectURL(i),
+ a = new Worker(d);
+ ((a.onerror = A),
+ (n = setTimeout(A, 200)),
+ (r = new ArrayBuffer(1)),
+ a.postMessage(r, [r]),
+ addTest('transferables', 0 === r.byteLength),
+ e());
+ } catch (s) {
+ A();
+ }
+ }),
+ Modernizr.addTest('getusermedia', !!prefixed('getUserMedia', navigator)),
+ Modernizr.addTest(
+ 'peerconnection',
+ !!prefixed('RTCPeerConnection', window)
+ ),
+ Modernizr.addTest('matchmedia', !!prefixed('matchMedia', window)),
+ (ModernizrProto.testAllProps = testAllProps),
+ Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', !0)),
+ Modernizr.addTest('csspseudoanimations', function () {
+ var A = !1;
+ if (!Modernizr.cssanimations || !window.getComputedStyle) return A;
+ var e = [
+ '@',
+ Modernizr._prefixes
+ .join('keyframes csspseudoanimations { from { font-size: 10px; } }@')
+ .replace(/\@$/, ''),
+ '#modernizr:before { content:" "; font-size:5px;',
+ Modernizr._prefixes.join('animation:csspseudoanimations 1ms infinite;'),
+ '}'
+ ].join('');
+ return (
+ Modernizr.testStyles(e, function (e) {
+ A =
+ '10px' ===
+ window.getComputedStyle(e, ':before').getPropertyValue('font-size');
+ }),
+ A
+ );
+ }),
+ Modernizr.addTest('appearance', testAllProps('appearance')),
+ Modernizr.addTest('backdropfilter', testAllProps('backdropFilter')),
+ Modernizr.addTest('backgroundcliptext', function () {
+ return testAllProps('backgroundClip', 'text');
+ }),
+ Modernizr.addTest('bgpositionxy', function () {
+ return (
+ testAllProps('backgroundPositionX', '3px', !0) &&
+ testAllProps('backgroundPositionY', '5px', !0)
+ );
+ }),
+ Modernizr.addTest(
+ 'bgrepeatround',
+ testAllProps('backgroundRepeat', 'round')
+ ),
+ Modernizr.addTest(
+ 'bgrepeatspace',
+ testAllProps('backgroundRepeat', 'space')
+ ),
+ Modernizr.addTest(
+ 'backgroundsize',
+ testAllProps('backgroundSize', '100%', !0)
+ ),
+ Modernizr.addTest('bgsizecover', testAllProps('backgroundSize', 'cover')),
+ Modernizr.addTest(
+ 'borderimage',
+ testAllProps('borderImage', 'url() 1', !0)
+ ),
+ Modernizr.addTest('borderradius', testAllProps('borderRadius', '0px', !0)),
+ Modernizr.addTest('boxshadow', testAllProps('boxShadow', '1px 1px', !0)),
+ Modernizr.addTest(
+ 'boxsizing',
+ testAllProps('boxSizing', 'border-box', !0) &&
+ (document.documentMode === undefined || document.documentMode > 7)
+ ),
+ (function () {
+ Modernizr.addTest('csscolumns', function () {
+ var A = !1,
+ e = testAllProps('columnCount');
+ try {
+ ((A = !!e), A && (A = new Boolean(A)));
+ } catch (t) {}
+ return A;
+ });
+ for (
+ var A,
+ e,
+ t = [
+ 'Width',
+ 'Span',
+ 'Fill',
+ 'Gap',
+ 'Rule',
+ 'RuleColor',
+ 'RuleStyle',
+ 'RuleWidth',
+ 'BreakBefore',
+ 'BreakAfter',
+ 'BreakInside'
+ ],
+ r = 0;
+ r < t.length;
+ r++
+ )
+ ((A = t[r].toLowerCase()),
+ (e = testAllProps('column' + t[r])),
+ ('breakbefore' === A || 'breakafter' === A || 'breakinside' == A) &&
+ (e = e || testAllProps(t[r])),
+ Modernizr.addTest('csscolumns.' + A, e));
+ })(),
+ Modernizr.addTest('ellipsis', testAllProps('textOverflow', 'ellipsis')),
+ Modernizr.addTest('cssfilters', function () {
+ if (Modernizr.supports) return testAllProps('filter', 'blur(2px)');
+ var A = createElement('a');
+ return (
+ (A.style.cssText = prefixes.join('filter:blur(2px); ')),
+ !!A.style.length &&
+ (document.documentMode === undefined || document.documentMode > 9)
+ );
+ }),
+ Modernizr.addTest('flexbox', testAllProps('flexBasis', '1px', !0)),
+ Modernizr.addTest(
+ 'flexboxlegacy',
+ testAllProps('boxDirection', 'reverse', !0)
+ ),
+ Modernizr.addTest('flexboxtweener', testAllProps('flexAlign', 'end', !0)),
+ Modernizr.addTest('flexwrap', testAllProps('flexWrap', 'wrap', !0)),
+ Modernizr.addAsyncTest(function () {
+ function A() {
+ function t() {
+ try {
+ var A = createElement('div'),
+ e = createElement('span'),
+ t = A.style,
+ r = 0,
+ n = 0,
+ o = !1,
+ i = document.body.firstElementChild || document.body.firstChild;
+ return (
+ A.appendChild(e),
+ (e.innerHTML =
+ 'Bacon ipsum dolor sit amet jerky velit in culpa hamburger et. Laborum dolor proident, enim dolore duis commodo et strip steak. Salami anim et, veniam consectetur dolore qui tenderloin jowl velit sirloin. Et ad culpa, fatback cillum jowl ball tip ham hock nulla short ribs pariatur aute. Pig pancetta ham bresaola, ut boudin nostrud commodo flank esse cow tongue culpa. Pork belly bresaola enim pig, ea consectetur nisi. Fugiat officia turkey, ea cow jowl pariatur ullamco proident do laborum velit sausage. Magna biltong sint tri-tip commodo sed bacon, esse proident aliquip. Ullamco ham sint fugiat, velit in enim sed mollit nulla cow ut adipisicing nostrud consectetur. Proident dolore beef ribs, laborum nostrud meatball ea laboris rump cupidatat labore culpa. Shankle minim beef, velit sint cupidatat fugiat tenderloin pig et ball tip. Ut cow fatback salami, bacon ball tip et in shank strip steak bresaola. In ut pork belly sed mollit tri-tip magna culpa veniam, short ribs qui in andouille ham consequat. Dolore bacon t-bone, velit short ribs enim strip steak nulla. Voluptate labore ut, biltong swine irure jerky. Cupidatat excepteur aliquip salami dolore. Ball tip strip steak in pork dolor. Ad in esse biltong. Dolore tenderloin exercitation ad pork loin t-bone, dolore in chicken ball tip qui pig. Ut culpa tongue, sint ribeye dolore ex shank voluptate hamburger. Jowl et tempor, boudin pork chop labore ham hock drumstick consectetur tri-tip elit swine meatball chicken ground round. Proident shankle mollit dolore. Shoulder ut duis t-bone quis reprehenderit. Meatloaf dolore minim strip steak, laboris ea aute bacon beef ribs elit shank in veniam drumstick qui. Ex laboris meatball cow tongue pork belly. Ea ball tip reprehenderit pig, sed fatback boudin dolore flank aliquip laboris eu quis. Beef ribs duis beef, cow corned beef adipisicing commodo nisi deserunt exercitation. Cillum dolor t-bone spare ribs, ham hock est sirloin. Brisket irure meatloaf in, boudin pork belly sirloin ball tip. Sirloin sint irure nisi nostrud aliqua. Nostrud nulla aute, enim officia culpa ham hock. Aliqua reprehenderit dolore sunt nostrud sausage, ea boudin pork loin ut t-bone ham tempor. Tri-tip et pancetta drumstick laborum. Ham hock magna do nostrud in proident. Ex ground round fatback, venison non ribeye in.'),
+ document.body.insertBefore(A, i),
+ (t.cssText =
+ 'position:absolute;top:0;left:0;width:5em;text-align:justify;text-justification:newspaper;'),
+ (r = e.offsetHeight),
+ (n = e.offsetWidth),
+ (t.cssText =
+ 'position:absolute;top:0;left:0;width:5em;text-align:justify;text-justification:newspaper;' +
+ prefixes.join('hyphens:auto; ')),
+ (o = e.offsetHeight != r || e.offsetWidth != n),
+ document.body.removeChild(A),
+ A.removeChild(e),
+ o
+ );
+ } catch (d) {
+ return !1;
+ }
+ }
+ function r(A, e) {
+ try {
+ var t = createElement('div'),
+ r = createElement('span'),
+ n = t.style,
+ o = 0,
+ i = !1,
+ d = !1,
+ a = !1,
+ s = document.body.firstElementChild || document.body.firstChild;
+ return (
+ (n.cssText =
+ 'position:absolute;top:0;left:0;overflow:visible;width:1.25em;'),
+ t.appendChild(r),
+ document.body.insertBefore(t, s),
+ (r.innerHTML = 'mm'),
+ (o = r.offsetHeight),
+ (r.innerHTML = 'm' + A + 'm'),
+ (d = r.offsetHeight > o),
+ e
+ ? ((r.innerHTML = 'm
m'),
+ (o = r.offsetWidth),
+ (r.innerHTML = 'm' + A + 'm'),
+ (a = r.offsetWidth > o))
+ : (a = !0),
+ d === !0 && a === !0 && (i = !0),
+ document.body.removeChild(t),
+ t.removeChild(r),
+ i
+ );
+ } catch (l) {
+ return !1;
+ }
+ }
+ function n(A) {
+ try {
+ var e,
+ t = createElement('input'),
+ r = createElement('div'),
+ n = 'lebowski',
+ o = !1,
+ i = document.body.firstElementChild || document.body.firstChild;
+ ((r.innerHTML = n + A + n),
+ document.body.insertBefore(r, i),
+ document.body.insertBefore(t, r),
+ t.setSelectionRange
+ ? (t.focus(), t.setSelectionRange(0, 0))
+ : t.createTextRange &&
+ ((e = t.createTextRange()),
+ e.collapse(!0),
+ e.moveEnd('character', 0),
+ e.moveStart('character', 0),
+ e.select()));
+ try {
+ window.find
+ ? (o = window.find(n + n))
+ : ((e = window.self.document.body.createTextRange()),
+ (o = e.findText(n + n)));
+ } catch (d) {
+ o = !1;
+ }
+ return (
+ document.body.removeChild(r), document.body.removeChild(t), o
+ );
+ } catch (d) {
+ return !1;
+ }
+ }
+ return document.body || document.getElementsByTagName('body')[0]
+ ? (addTest('csshyphens', function () {
+ if (!testAllProps('hyphens', 'auto', !0)) return !1;
+ try {
+ return t();
+ } catch (A) {
+ return !1;
+ }
+ }),
+ addTest('softhyphens', function () {
+ try {
+ return r('', !0) && r('', !1);
+ } catch (A) {
+ return !1;
+ }
+ }),
+ void addTest('softhyphensfind', function () {
+ try {
+ return n('') && n('');
+ } catch (A) {
+ return !1;
+ }
+ }))
+ : void setTimeout(A, e);
+ }
+ var e = 300;
+ setTimeout(A, e);
+ }),
+ Modernizr.addTest('cssmask', testAllProps('maskRepeat', 'repeat-x', !0)),
+ Modernizr.addTest(
+ 'overflowscrolling',
+ testAllProps('overflowScrolling', 'touch', !0)
+ ),
+ Modernizr.addTest(
+ 'cssreflections',
+ testAllProps('boxReflect', 'above', !0)
+ ),
+ Modernizr.addTest('cssresize', testAllProps('resize', 'both', !0)),
+ Modernizr.addTest(
+ 'shapes',
+ testAllProps('shapeOutside', 'content-box', !0)
+ ),
+ Modernizr.addTest('textalignlast', testAllProps('textAlignLast')),
+ Modernizr.addTest('csstransforms', function () {
+ return (
+ -1 === navigator.userAgent.indexOf('Android 2.') &&
+ testAllProps('transform', 'scale(1)', !0)
+ );
+ }),
+ Modernizr.addTest('csstransforms3d', function () {
+ var A = !!testAllProps('perspective', '1px', !0),
+ e = Modernizr._config.usePrefixes;
+ if (A && (!e || 'webkitPerspective' in docElement.style)) {
+ var t,
+ r = '#modernizr{width:0;height:0}';
+ (Modernizr.supports
+ ? (t = '@supports (perspective: 1px)')
+ : ((t = '@media (transform-3d)'),
+ e && (t += ',(-webkit-transform-3d)')),
+ (t +=
+ '{#modernizr{width:7px;height:18px;margin:0;padding:0;border:0}}'),
+ testStyles(r + t, function (e) {
+ A = 7 === e.offsetWidth && 18 === e.offsetHeight;
+ }));
+ }
+ return A;
+ }),
+ Modernizr.addTest('csstransitions', testAllProps('transition', 'all', !0)),
+ Modernizr.addTest('csspseudotransitions', function () {
+ var A = !1;
+ if (!Modernizr.csstransitions || !window.getComputedStyle) return A;
+ var e =
+ '#modernizr:before { content:" "; font-size:5px;' +
+ Modernizr._prefixes.join('transition:0s 100s;') +
+ '}#modernizr.trigger:before { font-size:10px; }';
+ return (
+ Modernizr.testStyles(e, function (e) {
+ (window.getComputedStyle(e, ':before').getPropertyValue('font-size'),
+ (e.className += 'trigger'),
+ (A =
+ '5px' ===
+ window
+ .getComputedStyle(e, ':before')
+ .getPropertyValue('font-size')));
+ }),
+ A
+ );
+ }),
+ Modernizr.addTest('userselect', testAllProps('userSelect', 'none', !0)),
+ testRunner(),
+ setClasses(classes),
+ delete ModernizrProto.addTest,
+ delete ModernizrProto.addAsyncTest);
+ for (var i = 0; i < Modernizr._q.length; i++) Modernizr._q[i]();
+ window.Modernizr = Modernizr;
+})(window, document);
diff --git a/test/js/p5_helpers.js b/test/js/p5_helpers.js
index 363c0d462e..27c08564dc 100644
--- a/test/js/p5_helpers.js
+++ b/test/js/p5_helpers.js
@@ -3,23 +3,25 @@ import p5 from '../../src/app.js';
export function promisedSketch(sketch_fn) {
var myInstance;
- var promise = new Promise(function(resolve, reject) {
- myInstance = new p5(function(sketch) {
+ var promise = new Promise(function (resolve, reject) {
+ myInstance = new p5(function (sketch) {
return sketch_fn(sketch, resolve, reject);
});
});
- promise.catch(function() {}).then(function() {
- myInstance.remove();
- });
+ promise
+ .catch(function () {})
+ .then(function () {
+ myInstance.remove();
+ });
return promise;
}
export function testSketchWithPromise(name, sketch_fn) {
- var test_fn = function() {
+ var test_fn = function () {
return promisedSketch(sketch_fn);
};
- test_fn.toString = function() {
+ test_fn.toString = function () {
return sketch_fn.toString();
};
return test(name, test_fn);
@@ -35,11 +37,11 @@ export function parallelSketches(sketch_fns) {
var resultPromises = [];
var endCallbacks = [];
for (let i = 0; i < sketch_fns.length; i++) {
- setupPromises[i] = new Promise(function(resolve) {
- resultPromises[i] = promisedSketch(function(sketch, _resolve, _reject) {
+ setupPromises[i] = new Promise(function (resolve) {
+ resultPromises[i] = promisedSketch(function (sketch, _resolve, _reject) {
sketch_fns[i](sketch, _resolve, _reject);
var old_setup = sketch.setup;
- sketch.setup = function() {
+ sketch.setup = function () {
if (old_setup) {
old_setup();
}
@@ -82,7 +84,7 @@ export function createP5Iframe(html) {
return {
elt: elt,
- teardown: function() {
+ teardown: function () {
elt.parentNode.removeChild(elt);
}
};
diff --git a/test/mockServiceWorker.js b/test/mockServiceWorker.js
index ec47a9a50a..0c970efc96 100644
--- a/test/mockServiceWorker.js
+++ b/test/mockServiceWorker.js
@@ -5,24 +5,23 @@
* Mock Service Worker.
* @see https://github.com/mswjs/msw
* - Please do NOT modify this file.
- * - Please do NOT serve this file on production.
*/
-const PACKAGE_VERSION = '2.7.0'
-const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
+const PACKAGE_VERSION = '2.15.0'
+const INTEGRITY_CHECKSUM = '03cb67ac84128e63d7cd722a6e5b7f1e'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()
-self.addEventListener('install', function () {
+addEventListener('install', function () {
self.skipWaiting()
})
-self.addEventListener('activate', function (event) {
+addEventListener('activate', function (event) {
event.waitUntil(self.clients.claim())
})
-self.addEventListener('message', async function (event) {
- const clientId = event.source.id
+addEventListener('message', async function (event) {
+ const clientId = Reflect.get(event.source || {}, 'id')
if (!clientId || !self.clients) {
return
@@ -72,11 +71,6 @@ self.addEventListener('message', async function (event) {
break
}
- case 'MOCK_DEACTIVATE': {
- activeClientIds.delete(clientId)
- break
- }
-
case 'CLIENT_CLOSED': {
activeClientIds.delete(clientId)
@@ -94,69 +88,104 @@ self.addEventListener('message', async function (event) {
}
})
-self.addEventListener('fetch', function (event) {
- const { request } = event
+addEventListener('fetch', function (event) {
+ const requestInterceptedAt = Date.now()
// Bypass navigation requests.
- if (request.mode === 'navigate') {
+ if (event.request.mode === 'navigate') {
return
}
// Opening the DevTools triggers the "only-if-cached" request
// that cannot be handled by the worker. Bypass such requests.
- if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
+ if (
+ event.request.cache === 'only-if-cached' &&
+ event.request.mode !== 'same-origin'
+ ) {
return
}
// Bypass all requests when there are no active clients.
// Prevents the self-unregistered worked from handling requests
- // after it's been deleted (still remains active until the next reload).
+ // after it's been terminated (still remains active until the next reload).
if (activeClientIds.size === 0) {
return
}
- // Generate unique request ID.
const requestId = crypto.randomUUID()
- event.respondWith(handleRequest(event, requestId))
+ event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
})
-async function handleRequest(event, requestId) {
+/**
+ * @param {FetchEvent} event
+ * @param {string} requestId
+ * @param {number} requestInterceptedAt
+ */
+async function handleRequest(event, requestId, requestInterceptedAt) {
const client = await resolveMainClient(event)
- const response = await getResponse(event, client, requestId)
+ const requestCloneForEvents = event.request.clone()
+ const response = await getResponse(
+ event,
+ client,
+ requestId,
+ requestInterceptedAt,
+ )
// Send back the response clone for the "response:*" life-cycle events.
// Ensure MSW is active and ready to handle the message, otherwise
// this message will pend indefinitely.
if (client && activeClientIds.has(client.id)) {
- ;(async function () {
- const responseClone = response.clone()
-
- sendToClient(
- client,
- {
- type: 'RESPONSE',
- payload: {
- requestId,
- isMockedResponse: IS_MOCKED_RESPONSE in response,
- type: responseClone.type,
- status: responseClone.status,
- statusText: responseClone.statusText,
- body: responseClone.body,
- headers: Object.fromEntries(responseClone.headers.entries()),
+ const serializedRequest = await serializeRequest(requestCloneForEvents)
+
+ // Omit the body of server-sent event stream responses.
+ // Cloning such responses would prevent client-side stream cancelations
+ // from reaching the original stream (a teed stream only cancels its
+ // source once both of its branches cancel) and would buffer the
+ // entire stream into the unconsumed clone indefinitely.
+ const isEventStreamResponse = response.headers
+ .get('content-type')
+ ?.toLowerCase()
+ .startsWith('text/event-stream')
+
+ // Clone the response so both the client and the library could consume it.
+ const responseClone = isEventStreamResponse ? null : response.clone()
+
+ sendToClient(
+ client,
+ {
+ type: 'RESPONSE',
+ payload: {
+ isMockedResponse: IS_MOCKED_RESPONSE in response,
+ request: {
+ id: requestId,
+ ...serializedRequest,
+ },
+ response: {
+ type: response.type,
+ status: response.status,
+ statusText: response.statusText,
+ headers: Object.fromEntries(response.headers.entries()),
+ body: responseClone ? responseClone.body : null,
},
},
- [responseClone.body],
- )
- })()
+ },
+ responseClone && responseClone.body
+ ? [serializedRequest.body, responseClone.body]
+ : [],
+ )
}
return response
}
-// Resolve the main client for the given event.
-// Client that issues a request doesn't necessarily equal the client
-// that registered the worker. It's with the latter the worker should
-// communicate with during the response resolving phase.
+/**
+ * Resolve the main client for the given event.
+ * Client that issues a request doesn't necessarily equal the client
+ * that registered the worker. It's with the latter the worker should
+ * communicate with during the response resolving phase.
+ * @param {FetchEvent} event
+ * @returns {Promise}
+ */
async function resolveMainClient(event) {
const client = await self.clients.get(event.clientId)
@@ -184,12 +213,17 @@ async function resolveMainClient(event) {
})
}
-async function getResponse(event, client, requestId) {
- const { request } = event
-
+/**
+ * @param {FetchEvent} event
+ * @param {Client | undefined} client
+ * @param {string} requestId
+ * @param {number} requestInterceptedAt
+ * @returns {Promise}
+ */
+async function getResponse(event, client, requestId, requestInterceptedAt) {
// Clone the request because it might've been already used
// (i.e. its body has been read and sent to the client).
- const requestClone = request.clone()
+ const requestClone = event.request.clone()
function passthrough() {
// Cast the request headers to a new Headers instance
@@ -230,29 +264,18 @@ async function getResponse(event, client, requestId) {
}
// Notify the client that a request has been intercepted.
- const requestBuffer = await request.arrayBuffer()
+ const serializedRequest = await serializeRequest(event.request)
const clientMessage = await sendToClient(
client,
{
type: 'REQUEST',
payload: {
id: requestId,
- url: request.url,
- mode: request.mode,
- method: request.method,
- headers: Object.fromEntries(request.headers.entries()),
- cache: request.cache,
- credentials: request.credentials,
- destination: request.destination,
- integrity: request.integrity,
- redirect: request.redirect,
- referrer: request.referrer,
- referrerPolicy: request.referrerPolicy,
- body: requestBuffer,
- keepalive: request.keepalive,
+ interceptedAt: requestInterceptedAt,
+ ...serializedRequest,
},
},
- [requestBuffer],
+ [serializedRequest.body],
)
switch (clientMessage.type) {
@@ -268,6 +291,12 @@ async function getResponse(event, client, requestId) {
return passthrough()
}
+/**
+ * @param {Client} client
+ * @param {any} message
+ * @param {Array} transferrables
+ * @returns {Promise}
+ */
function sendToClient(client, message, transferrables = []) {
return new Promise((resolve, reject) => {
const channel = new MessageChannel()
@@ -280,14 +309,18 @@ function sendToClient(client, message, transferrables = []) {
resolve(event.data)
}
- client.postMessage(
- message,
- [channel.port2].concat(transferrables.filter(Boolean)),
- )
+ client.postMessage(message, [
+ channel.port2,
+ ...transferrables.filter(Boolean),
+ ])
})
}
-async function respondWithMock(response) {
+/**
+ * @param {Response} response
+ * @returns {Response}
+ */
+function respondWithMock(response) {
// Setting response status code to 0 is a no-op.
// However, when responding with a "Response.error()", the produced Response
// instance will have status code set to 0. Since it's not possible to create
@@ -305,3 +338,24 @@ async function respondWithMock(response) {
return mockedResponse
}
+
+/**
+ * @param {Request} request
+ */
+async function serializeRequest(request) {
+ return {
+ url: request.url,
+ mode: request.mode,
+ method: request.method,
+ headers: Object.fromEntries(request.headers.entries()),
+ cache: request.cache,
+ credentials: request.credentials,
+ destination: request.destination,
+ integrity: request.integrity,
+ redirect: request.redirect,
+ referrer: request.referrer,
+ referrerPolicy: request.referrerPolicy,
+ body: await request.arrayBuffer(),
+ keepalive: request.keepalive,
+ }
+}
diff --git a/test/node/helpers.js b/test/node/helpers.js
index c2d4814378..d185d53e00 100644
--- a/test/node/helpers.js
+++ b/test/node/helpers.js
@@ -8,20 +8,20 @@ const c = 50;
const d = 150;
let result;
-suite('helpers/modeAdjust', function() {
- test('should set mode to corner', function() {
+suite('helpers/modeAdjust', function () {
+ test('should set mode to corner', function () {
result = helpers.modeAdjust(a, b, c, d, constants.CORNER);
expect(result).to.eql({ x: 100, y: 200, w: 50, h: 150 });
});
- test('should set mode to corners', function() {
+ test('should set mode to corners', function () {
result = helpers.modeAdjust(a, b, c, d, constants.CORNERS);
expect(result).to.eql({ x: 50, y: 150, w: 50, h: 50 });
});
- test('should set mode to radius', function() {
+ test('should set mode to radius', function () {
result = helpers.modeAdjust(a, b, c, d, constants.RADIUS);
expect(result).to.eql({ x: 50, y: 50, w: 100, h: 300 });
});
- test('should set mode to center', function() {
+ test('should set mode to center', function () {
result = helpers.modeAdjust(a, b, c, d, constants.CENTER);
expect(result).to.eql({ x: 75, y: 125, w: 50, h: 150 });
});
diff --git a/test/node/test-docs-preprocessor.js b/test/node/test-docs-preprocessor.js
index ba5c854ece..56511748e9 100644
--- a/test/node/test-docs-preprocessor.js
+++ b/test/node/test-docs-preprocessor.js
@@ -1,17 +1,17 @@
import { expect } from 'chai';
import preprocessor from '../../docs/preprocessor';
-suite('docs preprocessor', function() {
- suite('mergeOverloadedMethods()', function() {
+suite('docs preprocessor', function () {
+ suite('mergeOverloadedMethods()', function () {
const merge = preprocessor.mergeOverloadedMethods;
- const ensureMergeDoesNothing = function(data) {
+ const ensureMergeDoesNothing = function (data) {
const dataCopy = JSON.parse(JSON.stringify(data));
merge(dataCopy);
expect(dataCopy).to.eql(data);
};
- test('should merge methods with the same name', function() {
+ test('should merge methods with the same name', function () {
const data = {
classes: {
Bar: {},
@@ -70,7 +70,7 @@ suite('docs preprocessor', function() {
});
});
- test('should not merge methods from different classes', function() {
+ test('should not merge methods from different classes', function () {
ensureMergeDoesNothing({
classes: {
Bar: {},
@@ -84,7 +84,7 @@ suite('docs preprocessor', function() {
});
});
- test('should not merge properties', function() {
+ test('should not merge properties', function () {
ensureMergeDoesNothing({
classes: {
Bar: {},
@@ -99,10 +99,10 @@ suite('docs preprocessor', function() {
});
});
- suite('renderDescriptionsAsMarkdown', function() {
+ suite('renderDescriptionsAsMarkdown', function () {
const render = preprocessor.renderDescriptionsAsMarkdown;
- test('should work', function() {
+ test('should work', function () {
const data = {
modules: {},
classes: {
diff --git a/test/types/basic.ts b/test/types/basic.ts
index 7398596f74..0b662297a8 100644
--- a/test/types/basic.ts
+++ b/test/types/basic.ts
@@ -1,77 +1,73 @@
// Modified from https://openprocessing.org/sketch/2500100
-import p5 from '../../types/global'
+import p5 from '../../types/global';
-p5.disableFriendlyErrors = true
+p5.disableFriendlyErrors = true;
-let geom: p5.Geometry
-let geom2: p5.Geometry
+let geom: p5.Geometry;
+let geom2: p5.Geometry;
function setup() {
- createCanvas(windowWidth, windowHeight, WEBGL)
- geom2 = new p5.Geometry(1, 1, function() {
- this.vertices.push(createVector(0, 0, 0))
- this.vertices.push(createVector(1, 0, 0))
- this.vertices.push(createVector(1, 1, 0))
- this.faces.push([0, 1, 2])
- this.computeNormals()
- })
+ createCanvas(windowWidth, windowHeight, WEBGL);
+ geom2 = new p5.Geometry(1, 1, function () {
+ this.vertices.push(createVector(0, 0, 0));
+ this.vertices.push(createVector(1, 0, 0));
+ this.vertices.push(createVector(1, 1, 0));
+ this.faces.push([0, 1, 2]);
+ this.computeNormals();
+ });
}
function regenerate() {
- if (geom) {
- freeGeometry(geom)
- }
- geom = buildGeometry(() => {
- let n = round(random(5, 20))
- for (let i = 0; i <= n; i++) {
- push()
- translate(
- random(-1, 1)*width*0.05,
- map(i, 0, n, height*0.4, -height*0.4) + random(-1,1)*height*0.05
- )
- rotateX(PI/2 + random(-1, 1) * PI * 0.15)
- rotateZ(random(-1, 1) * PI * 0.15)
- torus(
- random(0.1, 0.3) * width,
- random(0.01, 0.05) * width,
- 50,
- 30
- )
- pop()
- }
- })
- geom.clearColors()
+ if (geom) {
+ freeGeometry(geom);
+ }
+ geom = buildGeometry(() => {
+ let n = round(random(5, 20));
+ for (let i = 0; i <= n; i++) {
+ push();
+ translate(
+ random(-1, 1) * width * 0.05,
+ map(i, 0, n, height * 0.4, -height * 0.4) +
+ random(-1, 1) * height * 0.05
+ );
+ rotateX(PI / 2 + random(-1, 1) * PI * 0.15);
+ rotateZ(random(-1, 1) * PI * 0.15);
+ torus(random(0.1, 0.3) * width, random(0.01, 0.05) * width, 50, 30);
+ pop();
+ }
+ });
+ geom.clearColors();
}
-let lastScene = -1
+let lastScene = -1;
function draw() {
- const period = 8000
-
- const ms = millis()
- const scene = floor(ms / period)
- if (scene !== lastScene) {
- regenerate()
- lastScene = scene
- }
-
- const t = (ms % period)/period
- background(0)
- orbitControl()
- const s = map(t, 0, 0.2, 0, 1, true) * map(t, 0.8, 1, 1, 0, true)
- directionalLight(s*255, s*255, s*255, -0.4, 0, 1)
- directionalLight(s*255, s*255, s*255, 0.4, 0, 1)
- directionalLight(s*255, s*255, s*255, 0, -0.4, 1)
- directionalLight(s*255, s*255, s*255, 0, 0.4, 1)
- noStroke()
- fill(100)
- specularMaterial(255)
- shininess(400)
- scale(0.8)
- rotateY(millis() * 0.0001)
- model(geom)
+ const period = 8000;
+
+ const ms = millis();
+ const scene = floor(ms / period);
+ if (scene !== lastScene) {
+ regenerate();
+ lastScene = scene;
+ }
+
+ const t = (ms % period) / period;
+ background(0);
+ orbitControl();
+ const s = map(t, 0, 0.2, 0, 1, true) * map(t, 0.8, 1, 1, 0, true);
+ directionalLight(s * 255, s * 255, s * 255, -0.4, 0, 1);
+ directionalLight(s * 255, s * 255, s * 255, 0.4, 0, 1);
+ directionalLight(s * 255, s * 255, s * 255, 0, -0.4, 1);
+ directionalLight(s * 255, s * 255, s * 255, 0, 0.4, 1);
+ noStroke();
+ fill(100);
+ specularMaterial(255);
+ shininess(400);
+ scale(0.8);
+ rotateY(millis() * 0.0001);
+ model(geom);
}
-function mousePressed(){
- //only type-checking the relevant properties exist on the mouseButton object type.
- const whatever = mouseButton.left || mouseButton.center || mouseButton.right;
-}
\ No newline at end of file
+function mousePressed() {
+ //only type-checking the relevant properties exist on the mouseButton object type.
+ const whatever = mouseButton.left || mouseButton.center || mouseButton.right;
+}
diff --git a/test/types/generics.ts b/test/types/generics.ts
index f59f575863..0540179d50 100644
--- a/test/types/generics.ts
+++ b/test/types/generics.ts
@@ -1,53 +1,56 @@
-import '../../types/global'
+import '../../types/global';
function setup() {
- noCanvas()
+ noCanvas();
const messages = [
{ content: 'Hello, world!' },
- { content: "How's it going?" },
- ]
- const message = random(messages)
+ { content: "How's it going?" }
+ ];
+ const message = random(messages);
// The types should fail if the result of random() is any
logMessage(message);
-
+
testShuffleMaintainsType();
}
// From: https://stackoverflow.com/a/50375286/62076
-type UnionToIntersection =
- (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
+type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (
+ k: infer I
+) => void
+ ? I
+ : never;
// If T is `any` a union of both side of the condition is returned.
-type UnionForAny = T extends never ? 'A' : 'B'
+type UnionForAny = T extends never ? 'A' : 'B';
// Returns true if type is any, or false for any other type.
type IsStrictlyAny =
- UnionToIntersection> extends never ? true : false
+ UnionToIntersection> extends never ? true : false;
-type NotAny = IsStrictlyAny extends true ? never : T
+type NotAny = IsStrictlyAny extends true ? never : T;
function logMessage(message: NotAny<{ content: string }>) {
- console.log(message)
+ console.log(message);
}
-
-
/** test that shuffle(arr) preserves arr type in return, not just any[] */
-function testShuffleMaintainsType(){
-
- type Expect = T;
- type Equal =
- (() => T extends X ? 1 : 2) extends
- (() => T extends Y ? 1 : 2) ? true : false;
-
- const shuffleResult1 = shuffle(["a", "b", "c"]);
- const shuffleResult2 = shuffle(["a", 10, null]);
+function testShuffleMaintainsType() {
+ type Expect = T;
+ type Equal =
+ (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2
+ ? true
+ : false;
+
+ const shuffleResult1 = shuffle(['a', 'b', 'c']);
+ const shuffleResult2 = shuffle(['a', 10, null]);
//check the signature with the optional boolean type-checks, too
- const shuffleResult3 = shuffle([10, 20, 30], true);
+ const shuffleResult3 = shuffle([10, 20, 30], true);
- type ShuffleTest1 = Expect>;
- type ShuffleTest2 = Expect>;
+ type ShuffleTest1 = Expect>;
+ type ShuffleTest2 = Expect<
+ Equal
+ >;
type ShuffleTest3 = Expect>;
}
diff --git a/test/types/instance.ts b/test/types/instance.ts
index cd643d7ef9..5a00b3e11b 100644
--- a/test/types/instance.ts
+++ b/test/types/instance.ts
@@ -1,20 +1,20 @@
-import P5 from '../../types/p5'
+import P5 from '../../types/p5';
-P5.disableFriendlyErrors = true
+P5.disableFriendlyErrors = true;
-const sketch = new P5((p) => {
- let g: P5.Graphics
- p.setup = function() {
- p.createCanvas(200, 200)
- g = p.createGraphics(200, 200)
- }
+const sketch = new P5(p => {
+ let g: P5.Graphics;
+ p.setup = function () {
+ p.createCanvas(200, 200);
+ g = p.createGraphics(200, 200);
+ };
- p.mouseMoved = function() {
+ p.mouseMoved = function () {
g.circle(p.mouseX, p.mouseY, 20);
- }
+ };
- p.draw = function() {
- p.clear()
- p.image(g, 0, 0)
- }
-})
+ p.draw = function () {
+ p.clear();
+ p.image(g, 0, 0);
+ };
+});
diff --git a/test/types/strands.ts b/test/types/strands.ts
index 36397cbd58..ce3cfd1ffd 100644
--- a/test/types/strands.ts
+++ b/test/types/strands.ts
@@ -1,7 +1,7 @@
// Based on p5.strands: Introduction to Shaders By Luke Plowden
// https://beta.p5js.org/tutorials/intro-to-p5-strands/
-import '../../types/global'
+import '../../types/global';
let starShader: p5.Shader;
let starStrokeShader: p5.Shader;
@@ -16,12 +16,12 @@ function fresnelShaderCallback() {
const fresnelBias = uniformFloat(-0.1);
const fresnelScale = uniformFloat(2);
- getCameraInputs((inputs) => {
+ getCameraInputs(inputs => {
let n = normalize(inputs.normal);
let v = normalize(-inputs.position);
let base = 1.0 - dot(n, v);
let fresnel = fresnelScale * pow(base, fresnelPower) + fresnelBias;
- let col = mix([0, 0, 0], [1, .5, .7], fresnel);
+ let col = mix([0, 0, 0], [1, 0.5, 0.7], fresnel);
inputs.color = [col, 1];
return inputs;
});
@@ -35,7 +35,7 @@ function starShaderCallback() {
const testSharedVector = sharedVector2();
const testVaryingVec = varyingVec2();
const testVaryingVector = varyingVector2();
-
+
function rand2(st) {
return fract(sin(dot(st, [12.9898, 78.233])) * 43758.5453123);
}
@@ -43,9 +43,9 @@ function starShaderCallback() {
function semiSphere() {
let id = instanceID();
let idx = instanceIndex;
- let theta = rand2([id, 0.1234]) * TWO_PI + time / 100000;
+ let theta = rand2([id, 0.1234]) * TWO_PI + time / 100000;
let phi = rand2([id, 3.321]) * PI + time / 50000;
-
+
let r = skyRadius;
r *= sin(phi);
let x = r * sin(phi) * cos(theta);
@@ -54,12 +54,12 @@ function starShaderCallback() {
return [x, y, z];
}
- getWorldInputs((inputs) => {
+ getWorldInputs(inputs => {
inputs.position += semiSphere();
return inputs;
});
- getObjectInputs((inputs) => {
+ getObjectInputs(inputs => {
let size = 1 + 0.5 * sin(time * 0.002 + instanceID());
inputs.position *= size;
return inputs;
@@ -67,7 +67,7 @@ function starShaderCallback() {
}
function pixelateShaderCallback() {
- const pixelCountX = uniformFloat(()=> 280);
+ const pixelCountX = uniformFloat(() => 280);
getColor((inputs, canvasContent) => {
const aspectRatio = inputs.canvasSize.x / inputs.canvasSize.y;
@@ -78,7 +78,7 @@ function pixelateShaderCallback() {
coord = floor(coord * pixelSize) / pixelSize;
let col = getTexture(canvasContent, coord);
- return col//[coord, 0, 1];
+ return col; //[coord, 0, 1];
});
}
@@ -90,52 +90,52 @@ function bloomShaderCallback() {
const originalCol = getTexture(preBlur, input.texCoord);
const intensity = max(originalCol, 0.1) * 12.2;
-
+
const bloom = originalCol + blurredCol * intensity;
return [bloom.rgb, 1];
});
}
-async function setup(){
+async function setup() {
createCanvas(800, 600, WEBGL);
pixelDensity(1);
- stars = buildGeometry(() => sphere(8, 4, 2))
+ stars = buildGeometry(() => sphere(8, 4, 2));
originalImage = createFramebuffer();
- starShader = baseMaterialShader().modify(starShaderCallback);
- starStrokeShader = baseStrokeShader().modify(starShaderCallback)
+ starShader = baseMaterialShader().modify(starShaderCallback);
+ starStrokeShader = baseStrokeShader().modify(starShaderCallback);
fresnelShader = baseColorShader().modify(fresnelShaderCallback);
bloomShader = baseFilterShader().modify(bloomShaderCallback);
pixelateShader = baseFilterShader().modify(pixelateShaderCallback);
}
-function draw(){
+function draw() {
originalImage.begin();
background(0);
orbitControl();
- push()
- strokeWeight(2)
- stroke(255,0,0)
- rotateX(PI/2 + millis() * 0.0005);
- fill(255,100, 150)
- strokeShader(starStrokeShader)
+ push();
+ strokeWeight(2);
+ stroke(255, 0, 0);
+ rotateX(PI / 2 + millis() * 0.0005);
+ fill(255, 100, 150);
+ strokeShader(starStrokeShader);
shader(starShader);
model(stars, 1000);
- pop()
+ pop();
- push()
- shader(fresnelShader)
- noStroke()
+ push();
+ shader(fresnelShader);
+ noStroke();
sphere(90);
filter(pixelateShader);
- pop()
+ pop();
originalImage.end();
-
- imageMode(CENTER)
- image(originalImage, 0, 0)
-
- filter(BLUR, 15)
+
+ imageMode(CENTER);
+ image(originalImage, 0, 0);
+
+ filter(BLUR, 15);
filter(bloomShader);
}
diff --git a/test/types/typography.ts b/test/types/typography.ts
index 972e43d0df..b150198ca5 100644
--- a/test/types/typography.ts
+++ b/test/types/typography.ts
@@ -1,73 +1,87 @@
// From https://openprocessing.org/sketch/2523015
-import '../../types/global'
+import '../../types/global';
-let font: p5.Font
-const txt = 'p5.js'
+let font: p5.Font;
+const txt = 'p5.js';
async function setup() {
- createCanvas(windowWidth, windowHeight);
- font = await loadFont('https://fonts.gstatic.com/s/sniglet/v17/cIf4MaFLtkE3UjaJ_ImHRGEsnIJkWL4.ttf')
+ createCanvas(windowWidth, windowHeight);
+ font = await loadFont(
+ 'https://fonts.gstatic.com/s/sniglet/v17/cIf4MaFLtkE3UjaJ_ImHRGEsnIJkWL4.ttf'
+ );
}
-let prevTxt = ''
-let prevTxtTime = 0
+let prevTxt = '';
+let prevTxtTime = 0;
function draw() {
- if (txt !== prevTxt) {
- prevTxt = txt
- prevTxtTime = millis()
- }
- const progress = pow(map(millis(), prevTxtTime, prevTxtTime + 2000, 0, 1, true), 0.5)
-
- const contours = font.textToContours(txt, 0, 0, { sampleFactor: 1 })
-
- background(0)
- textAlign(CENTER, CENTER)
- textSize(120)
- textFont(font)
- translate(width/2, height/2)
- scale(min(width, height)/300)
- const w = max(10, fontWidth(txt))
- scale((width * 0.2) / w)
-
- beginShape()
- for (const contour of contours) {
- beginContour()
- for (const pt of contour) {
- vertex(pt.x, pt.y)
- }
- endContour(CLOSE)
- }
- endShape()
-
- push()
- strokeWeight(0.5)
- stroke('rgb(255,127,228)')
- noFill()
+ if (txt !== prevTxt) {
+ prevTxt = txt;
+ prevTxtTime = millis();
+ }
+ const progress = pow(
+ map(millis(), prevTxtTime, prevTxtTime + 2000, 0, 1, true),
+ 0.5
+ );
- beginShape(LINES)
- for (const contour of contours) {
- const pts = contour.map((v) => createVector(v.x, v.y))
- if (pts[0].dist(pts.at(-1)) === 0) pts.pop()
- const dists = pts.map((pt, i) => max(1e-6, pt.dist(pts[(i+1)%pts.length])))
-
- let tangents = pts.map((v, i) => pts[(i+1)%pts.length].copy().sub(v).div(dists[i]))
- for (let it = 0; it < 2; it++) {
- tangents = tangents.map(
- (tangent, i) =>
- tangent.copy()
- .add(tangents[(i-1+pts.length)%pts.length])
- .add(tangents[(i+1)%pts.length])
- .mult(1/3)
- )
- }
-
- const ks = tangents.map((t, i) => tangents[(i+1)%pts.length].copy().sub(t))
-
- pts.forEach((pt, i) => {
- vertex(pt.x, pt.y)
- vertex(pt.x + ks[i].x * -120 * progress, pt.y + ks[i].y * -120 * progress)
- })
- }
- endShape()
- pop()
+ const contours = font.textToContours(txt, 0, 0, { sampleFactor: 1 });
+
+ background(0);
+ textAlign(CENTER, CENTER);
+ textSize(120);
+ textFont(font);
+ translate(width / 2, height / 2);
+ scale(min(width, height) / 300);
+ const w = max(10, fontWidth(txt));
+ scale((width * 0.2) / w);
+
+ beginShape();
+ for (const contour of contours) {
+ beginContour();
+ for (const pt of contour) {
+ vertex(pt.x, pt.y);
+ }
+ endContour(CLOSE);
+ }
+ endShape();
+
+ push();
+ strokeWeight(0.5);
+ stroke('rgb(255,127,228)');
+ noFill();
+
+ beginShape(LINES);
+ for (const contour of contours) {
+ const pts = contour.map(v => createVector(v.x, v.y));
+ if (pts[0].dist(pts.at(-1)) === 0) pts.pop();
+ const dists = pts.map((pt, i) =>
+ max(1e-6, pt.dist(pts[(i + 1) % pts.length]))
+ );
+
+ let tangents = pts.map((v, i) =>
+ pts[(i + 1) % pts.length].copy().sub(v).div(dists[i])
+ );
+ for (let it = 0; it < 2; it++) {
+ tangents = tangents.map((tangent, i) =>
+ tangent
+ .copy()
+ .add(tangents[(i - 1 + pts.length) % pts.length])
+ .add(tangents[(i + 1) % pts.length])
+ .mult(1 / 3)
+ );
+ }
+
+ const ks = tangents.map((t, i) =>
+ tangents[(i + 1) % pts.length].copy().sub(t)
+ );
+
+ pts.forEach((pt, i) => {
+ vertex(pt.x, pt.y);
+ vertex(
+ pt.x + ks[i].x * -120 * progress,
+ pt.y + ks[i].y * -120 * progress
+ );
+ });
+ }
+ endShape();
+ pop();
}
diff --git a/test/types/webgl-pixels.ts b/test/types/webgl-pixels.ts
index 8c60186372..4b535604d7 100644
--- a/test/types/webgl-pixels.ts
+++ b/test/types/webgl-pixels.ts
@@ -1,211 +1,244 @@
// From https://openprocessing.org/sketch/2308573
-import '../../types/global'
+import '../../types/global';
// Fake matter.js import
-declare const Matter: any
+declare const Matter: any;
-let engine: any
-let blobs: Blob[] = []
-let metaballShader: p5.Shader
-let spheremap: p5.Image
-let renderer: p5.RendererGL
+let engine: any;
+let blobs: Blob[] = [];
+let metaballShader: p5.Shader;
+let spheremap: p5.Image;
+let renderer: p5.RendererGL;
async function setup() {
- spheremap = await loadImage('https://deckard.openprocessing.org/user67809/visual2181338/h987a85d77bacbc3b232fb87ce6fe440a/dusseldorf_bridge.jpg')
- renderer = createCanvas(600, 600, WEBGL)
- metaballShader = createShader(vert, frag)
- setupScene()
- blobs.push(new Blob(random(-1,1)*100, 50, 100, '#f3e17e'))
- blobs.push(new Blob(random(-1,1)*100, -150, 100, '#dd483c'))
- blobs.push(new Blob(random(-1,1)*100, -350, 50, '#4b8a5f'))
- blobs.push(new Blob(random(-1,1)*100, -550, 50, '#0d150b'))
+ spheremap = await loadImage(
+ 'https://deckard.openprocessing.org/user67809/visual2181338/h987a85d77bacbc3b232fb87ce6fe440a/dusseldorf_bridge.jpg'
+ );
+ renderer = createCanvas(600, 600, WEBGL);
+ metaballShader = createShader(vert, frag);
+ setupScene();
+ blobs.push(new Blob(random(-1, 1) * 100, 50, 100, '#f3e17e'));
+ blobs.push(new Blob(random(-1, 1) * 100, -150, 100, '#dd483c'));
+ blobs.push(new Blob(random(-1, 1) * 100, -350, 50, '#4b8a5f'));
+ blobs.push(new Blob(random(-1, 1) * 100, -550, 50, '#0d150b'));
}
function setupScene() {
- engine = Matter.Engine.create()
-
- const ground = Matter.Bodies.rectangle(0, height / 2 + 30, width, 60, {
- isStatic: true,
- })
- const wallLeft = Matter.Bodies.rectangle(-width/2 - 30, 0, 60, 3 * height, {
- isStatic: true,
- })
- const wallRight = Matter.Bodies.rectangle(width/2 + 30, 0, 60, 3 * height, {
- isStatic: true,
- })
- Matter.World.add(engine.world, [ground, wallLeft, wallRight])
+ engine = Matter.Engine.create();
+
+ const ground = Matter.Bodies.rectangle(0, height / 2 + 30, width, 60, {
+ isStatic: true
+ });
+ const wallLeft = Matter.Bodies.rectangle(-width / 2 - 30, 0, 60, 3 * height, {
+ isStatic: true
+ });
+ const wallRight = Matter.Bodies.rectangle(width / 2 + 30, 0, 60, 3 * height, {
+ isStatic: true
+ });
+ Matter.World.add(engine.world, [ground, wallLeft, wallRight]);
}
function draw() {
- background('#faf8e2')
- // translate(width/2, height/2)
-
- for (const blob of blobs) {
- blob.update()
- }
- Matter.Engine.update(engine, 1000 / 60)
-
- for (const blob of blobs) {
- blob.drawBlob()
- }
+ background('#faf8e2');
+ // translate(width/2, height/2)
+
+ for (const blob of blobs) {
+ blob.update();
+ }
+ Matter.Engine.update(engine, 1000 / 60);
+
+ for (const blob of blobs) {
+ blob.drawBlob();
+ }
}
-const BLOB_NODE_SIZE = 20
-const BLOB_NODE_R = 15
-const BLOB_NODE_AREA = Math.PI * BLOB_NODE_SIZE * BLOB_NODE_SIZE
+const BLOB_NODE_SIZE = 20;
+const BLOB_NODE_R = 15;
+const BLOB_NODE_AREA = Math.PI * BLOB_NODE_SIZE * BLOB_NODE_SIZE;
class Blob {
- c: p5.Color
- nodes: any[]
- springs: any[]
- tex: p5.Image
-
- constructor(x, y, r, c) {
- this.nodes = []
- this.springs = []
- this.c = color(c)
- this.tex = createImage(20, 20)
- this.tex.loadPixels()
- for (let i = 0; i < this.tex.pixels.length; i++) {
- this.tex.pixels[i] = 255
- }
+ c: p5.Color;
+ nodes: any[];
+ springs: any[];
+ tex: p5.Image;
+
+ constructor(x, y, r, c) {
+ this.nodes = [];
+ this.springs = [];
+ this.c = color(c);
+ this.tex = createImage(20, 20);
+ this.tex.loadPixels();
+ for (let i = 0; i < this.tex.pixels.length; i++) {
+ this.tex.pixels[i] = 255;
+ }
// @ts-ignore
- renderer.getTexture(this.tex).setInterpolation(NEAREST, NEAREST)
-
- const a = PI * r * r
- const numBlobs = ceil(a / BLOB_NODE_AREA)
-
- while (this.nodes.length < numBlobs) {
- const rx = random(-r, r)
- const ry = random(-r, r)
- if (Math.hypot(rx, ry) > r) continue
-
- const vert = Matter.Bodies.circle(x + rx, y + ry, BLOB_NODE_R, { inertia: Infinity, friction: 0.015 })
- this.nodes.push(vert)
- }
-
- Matter.World.add(engine.world, this.nodes)
- }
-
- bin(x: number, y: number) {
- return [round(x/80), round(y/80)]
- }
-
- nodeBin(node) {
- return this.bin(node.position.x, node.position.y)
- }
-
- adjacentBins(node) {
- const [x, y] = this.nodeBin(node)
- const bins: [number, number][] = []
- for (const dx of [-1, 0, 1]) {
- for (const dy of [-1, 0, 1]) {
- bins.push([x + dx, y + dy])
- }
- }
- return bins
- }
-
- binKey(bin) {
- return bin.join(',')
- }
-
- binnedNodes() {
- const bins = {}
- for (const node of this.nodes) {
- const binKey = this.binKey(this.nodeBin(node))
- if (!bins[binKey]) {
- bins[binKey] = []
- }
- bins[binKey].push(node)
- }
- return bins
- }
-
- update() {
- Matter.World.remove(engine.world, this.springs)
- this.springs = []
- const bins = this.binnedNodes()
- for (const node of this.nodes) {
- const binsToCheck = this.adjacentBins(node)
- for (const bin of binsToCheck) {
- const key = this.binKey(bin)
- if (!bins[key]) continue
- for (const other of bins[key]) {
- if (other === node) continue
- this.springs.push(Matter.Constraint.create({
- bodyA: node,
- pointA: { x: 0, y: 0 },
- bodyB: other,
- pointB: { x: 0, y: 0 },
- stiffness: map(
- Math.hypot(node.position.x - other.position.x, node.position.y - other.position.y),
- 0, 12*BLOB_NODE_SIZE,
- 0.02, 0.03,
- true
- ),
- damping: 0.001,
- // length: 0,
- length: max(
- 2 * BLOB_NODE_SIZE,
- Math.hypot(node.position.x - other.position.x, node.position.y - other.position.y) * 0.975
- ),
- }))
- }
- }
- }
- Matter.World.add(engine.world, this.springs)
- }
-
- drawBlob() {
- const minX = Math.min(...this.nodes.map((n) => n.position.x)) - 4 * BLOB_NODE_SIZE
- const maxX = Math.max(...this.nodes.map((n) => n.position.x)) + 4 * BLOB_NODE_SIZE
- const minY = Math.min(...this.nodes.map((n) => n.position.y)) - 4 * BLOB_NODE_SIZE
- const maxY = Math.max(...this.nodes.map((n) => n.position.y)) + 4 * BLOB_NODE_SIZE
- const x = (maxX + minX)/2
- const y = (maxY + minY)/2
- const w = maxX - minX
- const h = maxY - minY
-
- this.nodes.forEach((node, i) => {
- this.tex.pixels[i * 4 + 0] = map(node.position.x, minX, maxX, 0, 255, true)
- this.tex.pixels[i * 4 + 1] = map(node.position.y, minY, maxY, 0, 255, true)
- })
- this.tex.updatePixels()
-
- push()
- translate(x, y)
- noStroke()
- shader(metaballShader)
- metaballShader.setUniform('bbox', [minX, minY, maxX, maxY])
- metaballShader.setUniform('k', BLOB_NODE_SIZE * 3)
- metaballShader.setUniform('numNodes', this.nodes.length)
- metaballShader.setUniform('data', this.tex)
- metaballShader.setUniform('r', BLOB_NODE_R)
+ renderer.getTexture(this.tex).setInterpolation(NEAREST, NEAREST);
+
+ const a = PI * r * r;
+ const numBlobs = ceil(a / BLOB_NODE_AREA);
+
+ while (this.nodes.length < numBlobs) {
+ const rx = random(-r, r);
+ const ry = random(-r, r);
+ if (Math.hypot(rx, ry) > r) continue;
+
+ const vert = Matter.Bodies.circle(x + rx, y + ry, BLOB_NODE_R, {
+ inertia: Infinity,
+ friction: 0.015
+ });
+ this.nodes.push(vert);
+ }
+
+ Matter.World.add(engine.world, this.nodes);
+ }
+
+ bin(x: number, y: number) {
+ return [round(x / 80), round(y / 80)];
+ }
+
+ nodeBin(node) {
+ return this.bin(node.position.x, node.position.y);
+ }
+
+ adjacentBins(node) {
+ const [x, y] = this.nodeBin(node);
+ const bins: [number, number][] = [];
+ for (const dx of [-1, 0, 1]) {
+ for (const dy of [-1, 0, 1]) {
+ bins.push([x + dx, y + dy]);
+ }
+ }
+ return bins;
+ }
+
+ binKey(bin) {
+ return bin.join(',');
+ }
+
+ binnedNodes() {
+ const bins = {};
+ for (const node of this.nodes) {
+ const binKey = this.binKey(this.nodeBin(node));
+ if (!bins[binKey]) {
+ bins[binKey] = [];
+ }
+ bins[binKey].push(node);
+ }
+ return bins;
+ }
+
+ update() {
+ Matter.World.remove(engine.world, this.springs);
+ this.springs = [];
+ const bins = this.binnedNodes();
+ for (const node of this.nodes) {
+ const binsToCheck = this.adjacentBins(node);
+ for (const bin of binsToCheck) {
+ const key = this.binKey(bin);
+ if (!bins[key]) continue;
+ for (const other of bins[key]) {
+ if (other === node) continue;
+ this.springs.push(
+ Matter.Constraint.create({
+ bodyA: node,
+ pointA: { x: 0, y: 0 },
+ bodyB: other,
+ pointB: { x: 0, y: 0 },
+ stiffness: map(
+ Math.hypot(
+ node.position.x - other.position.x,
+ node.position.y - other.position.y
+ ),
+ 0,
+ 12 * BLOB_NODE_SIZE,
+ 0.02,
+ 0.03,
+ true
+ ),
+ damping: 0.001,
+ // length: 0,
+ length: max(
+ 2 * BLOB_NODE_SIZE,
+ Math.hypot(
+ node.position.x - other.position.x,
+ node.position.y - other.position.y
+ ) * 0.975
+ )
+ })
+ );
+ }
+ }
+ }
+ Matter.World.add(engine.world, this.springs);
+ }
+
+ drawBlob() {
+ const minX =
+ Math.min(...this.nodes.map(n => n.position.x)) - 4 * BLOB_NODE_SIZE;
+ const maxX =
+ Math.max(...this.nodes.map(n => n.position.x)) + 4 * BLOB_NODE_SIZE;
+ const minY =
+ Math.min(...this.nodes.map(n => n.position.y)) - 4 * BLOB_NODE_SIZE;
+ const maxY =
+ Math.max(...this.nodes.map(n => n.position.y)) + 4 * BLOB_NODE_SIZE;
+ const x = (maxX + minX) / 2;
+ const y = (maxY + minY) / 2;
+ const w = maxX - minX;
+ const h = maxY - minY;
+
+ this.nodes.forEach((node, i) => {
+ this.tex.pixels[i * 4 + 0] = map(
+ node.position.x,
+ minX,
+ maxX,
+ 0,
+ 255,
+ true
+ );
+ this.tex.pixels[i * 4 + 1] = map(
+ node.position.y,
+ minY,
+ maxY,
+ 0,
+ 255,
+ true
+ );
+ });
+ this.tex.updatePixels();
+
+ push();
+ translate(x, y);
+ noStroke();
+ shader(metaballShader);
+ metaballShader.setUniform('bbox', [minX, minY, maxX, maxY]);
+ metaballShader.setUniform('k', BLOB_NODE_SIZE * 3);
+ metaballShader.setUniform('numNodes', this.nodes.length);
+ metaballShader.setUniform('data', this.tex);
+ metaballShader.setUniform('r', BLOB_NODE_R);
// TODO: make this a public API
// @ts-ignore
- metaballShader.setUniform('c', this.c.array())
- metaballShader.setUniform('spheremap', spheremap)
- plane(w, h)
- pop()
- }
-
- draw2D() {
- fill(this.c)
- stroke(this.c)
- strokeWeight(2 * BLOB_NODE_R)
- strokeJoin(ROUND)
- const hull = convexHull(this.nodes.map(n => n.position))
- beginShape()
- for (const { x, y } of hull) vertex(x, y)
- endShape(CLOSE)
-
- noStroke()
- fill(0)
- for (const node of this.nodes) {
- circle(node.position.x, node.position.y, BLOB_NODE_R * 2)
- }
- }
+ metaballShader.setUniform('c', this.c.array());
+ metaballShader.setUniform('spheremap', spheremap);
+ plane(w, h);
+ pop();
+ }
+
+ draw2D() {
+ fill(this.c);
+ stroke(this.c);
+ strokeWeight(2 * BLOB_NODE_R);
+ strokeJoin(ROUND);
+ const hull = convexHull(this.nodes.map(n => n.position));
+ beginShape();
+ for (const { x, y } of hull) vertex(x, y);
+ endShape(CLOSE);
+
+ noStroke();
+ fill(0);
+ for (const node of this.nodes) {
+ circle(node.position.x, node.position.y, BLOB_NODE_R * 2);
+ }
+ }
}
let vert = `#version 300 es
@@ -232,7 +265,7 @@ void main() {
// Pass along data to the fragment shader
vTexCoord = aTexCoord;
-}`
+}`;
let frag = `#version 300 es
precision highp float;
@@ -310,39 +343,39 @@ void main() {
// outColor = normal;
fragColor = vec4(outColor, 1.) * (1. - smoothstep(0., 0.01, dist));
-}`
+}`;
const comparison = (a: p5.Vector, b: p5.Vector) => {
- return a.x == b.x ? a.y - b.y : a.x - b.x
-}
+ return a.x == b.x ? a.y - b.y : a.x - b.x;
+};
const cross = (a: p5.Vector, b: p5.Vector, o: p5.Vector) => {
- return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
-}
+ return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
+};
function convexHull(points: p5.Vector[]) {
- points.sort(comparison)
- const L: p5.Vector[] = []
+ points.sort(comparison);
+ const L: p5.Vector[] = [];
for (let i = 0; i < points.length; i++) {
while (
L.length >= 2 &&
cross(L[L.length - 2], L[L.length - 1], points[i]) <= 0
) {
- L.pop()
+ L.pop();
}
- L.push(points[i])
+ L.push(points[i]);
}
- const U: p5.Vector[] = []
+ const U: p5.Vector[] = [];
for (let i = points.length - 1; i >= 0; i--) {
while (
U.length >= 2 &&
cross(U[U.length - 2], U[U.length - 1], points[i]) <= 0
) {
- U.pop()
+ U.pop();
}
- U.push(points[i])
+ U.push(points[i]);
}
- L.pop()
- U.pop()
- return L.concat(U)
+ L.pop();
+ U.pop();
+ return L.concat(U);
}
diff --git a/test/types/webgpu.ts b/test/types/webgpu.ts
index 7c9016cdf1..9c003cb892 100644
--- a/test/types/webgpu.ts
+++ b/test/types/webgpu.ts
@@ -1,4 +1,4 @@
-import p5 from '../../types/global'
+import p5 from '../../types/global';
async function setup() {
const renderer: p5.Renderer = await createCanvas(100, 100, WEBGPU);
diff --git a/test/unit/accessibility/describe.js b/test/unit/accessibility/describe.js
index 31676179ff..427aa16ce0 100644
--- a/test/unit/accessibility/describe.js
+++ b/test/unit/accessibility/describe.js
@@ -1,25 +1,25 @@
import { mockP5, mockP5Prototype } from '../../js/mocks';
import describe from '../../../src/accessibility/describe';
-suite('describe', function() {
+suite('describe', function () {
const myID = 'myCanvasID';
- beforeAll(function() {
+ beforeAll(function () {
describe(mockP5, mockP5Prototype);
mockP5Prototype.LABEL = 'label';
mockP5Prototype.FALLBACK = 'fallback';
});
- suite('p5.prototype.describe', function() {
- test('should be a function', function() {
+ suite('p5.prototype.describe', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.describe);
assert.typeOf(mockP5Prototype.describe, 'function');
});
- test('err when LABEL at param #0', function() {
+ test('err when LABEL at param #0', function () {
assert.throws(
- function() {
+ function () {
mockP5Prototype.describe(mockP5Prototype.LABEL);
},
Error,
@@ -27,19 +27,19 @@ suite('describe', function() {
);
});
- test('should create description as fallback', function() {
+ test('should create description as fallback', function () {
mockP5Prototype.describe('a');
let actual = document.getElementById(myID + '_fallbackDesc');
assert.deepEqual(actual.innerHTML, 'a.');
});
- test('should not add extra period if string ends in "."', function() {
+ test('should not add extra period if string ends in "."', function () {
mockP5Prototype.describe('A.');
let actual = document.getElementById(myID + '_fallbackDesc');
assert.deepEqual(actual.innerHTML, 'A.');
});
- test('should not add period if string ends in "!" or "?', function() {
+ test('should not add period if string ends in "!" or "?', function () {
mockP5Prototype.describe('A!');
let actual = document.getElementById(myID + '_fallbackDesc');
if (actual.innerHTML === 'A!') {
@@ -50,21 +50,21 @@ suite('describe', function() {
}
});
- test('should create description when called after describeElement()', function() {
+ test('should create description when called after describeElement()', function () {
mockP5Prototype.describeElement('b', 'c');
mockP5Prototype.describe('a');
let actual = document.getElementById(myID + '_fallbackDesc');
assert.deepEqual(actual.innerHTML, 'a.');
});
- test('should create Label adjacent to canvas', function() {
+ test('should create Label adjacent to canvas', function () {
mockP5Prototype.describe('a', mockP5Prototype.LABEL);
let actual = document.getElementById(myID + '_labelDesc');
assert.deepEqual(actual.innerHTML, 'a.');
});
- test('should create Label adjacent to canvas when label of element already exists', function() {
+ test('should create Label adjacent to canvas when label of element already exists', function () {
mockP5Prototype.describeElement('ba', 'c', mockP5Prototype.LABEL);
mockP5Prototype.describe('a', mockP5Prototype.LABEL);
let actual = document.getElementById(myID + '_labelDesc');
@@ -72,15 +72,15 @@ suite('describe', function() {
});
});
- suite('p5.prototype.describeElement', function() {
- test('should be a function', function() {
+ suite('p5.prototype.describeElement', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.describeElement);
assert.typeOf(mockP5Prototype.describeElement, 'function');
});
- test('err when LABEL at param #0', function() {
+ test('err when LABEL at param #0', function () {
assert.throws(
- function() {
+ function () {
mockP5Prototype.describeElement(mockP5Prototype.LABEL, 'b');
},
Error,
@@ -88,9 +88,9 @@ suite('describe', function() {
);
});
- test('err when LABEL at param #1', function() {
+ test('err when LABEL at param #1', function () {
assert.throws(
- function() {
+ function () {
mockP5Prototype.describeElement('a', mockP5Prototype.LABEL);
},
Error,
@@ -98,19 +98,19 @@ suite('describe', function() {
);
});
- test('should create element description as fallback', function() {
+ test('should create element description as fallback', function () {
mockP5Prototype.describeElement('az', 'b');
let actual = document.getElementById(myID + '_fte_az').innerHTML;
assert.deepEqual(actual, 'az: b. ');
});
- test('should not add extra ":" if element name ends in colon', function() {
+ test('should not add extra ":" if element name ends in colon', function () {
mockP5Prototype.describeElement('ab:', 'b.');
let actual = document.getElementById(myID + '_fte_ab').innerHTML;
assert.deepEqual(actual, 'ab: b. ');
});
- test('should replace ";", ",", "." for ":" in element name', function() {
+ test('should replace ";", ",", "." for ":" in element name', function () {
let actual;
mockP5Prototype.describeElement('ac;', 'b.');
if (
@@ -129,20 +129,20 @@ suite('describe', function() {
}
});
- test('should create element description when called after describe()', function() {
+ test('should create element description when called after describe()', function () {
mockP5Prototype.describe('c');
mockP5Prototype.describeElement('af', 'b');
let actual = document.getElementById(myID + '_fte_af').innerHTML;
assert.deepEqual(actual, 'af: b. ');
});
- test('should create element label adjacent to canvas', function() {
+ test('should create element label adjacent to canvas', function () {
mockP5Prototype.describeElement('ag', 'b', mockP5Prototype.LABEL);
const actual = document.getElementById(myID + '_lte_ag').innerHTML;
assert.deepEqual(actual, 'ag: b. ');
});
- test('should create element label adjacent to canvas when called after describe()', function() {
+ test('should create element label adjacent to canvas when called after describe()', function () {
mockP5Prototype.describe('c', mockP5Prototype.LABEL);
mockP5Prototype.describeElement('ah:', 'b', mockP5Prototype.LABEL);
const actual = document.getElementById(myID + '_lte_ah').innerHTML;
diff --git a/test/unit/accessibility/outputs.js b/test/unit/accessibility/outputs.js
index 167888bf1a..3da3291c6a 100644
--- a/test/unit/accessibility/outputs.js
+++ b/test/unit/accessibility/outputs.js
@@ -4,24 +4,24 @@ import textOutput from '../../../src/accessibility/textOutput';
import p5 from '../../../src/app.js';
// TODO: Is it possible to test this without a runtime?
-suite('outputs', function() {
+suite('outputs', function () {
let myID = 'myCanvasID';
- beforeAll(function() {
+ beforeAll(function () {
outputs(mockP5, mockP5Prototype);
textOutput(mockP5, mockP5Prototype);
});
- suite('p5.prototype.textOutput', function() {
- test('should be a function', function() {
+ suite('p5.prototype.textOutput', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.textOutput);
assert.typeOf(mockP5Prototype.textOutput, 'function');
});
- test('should not break for webgl', async function() {
+ test('should not break for webgl', async function () {
await new Promise(res => {
new p5(p => {
- p.setup = function() {
+ p.setup = function () {
p.createCanvas(50, 50, p.WEBGL);
p.textOutput();
p.circle(0, 0, 20);
@@ -31,10 +31,10 @@ suite('outputs', function() {
});
});
- test('should not break for webgl', async function() {
+ test('should not break for webgl', async function () {
await new Promise(res => {
new p5(p => {
- p.setup = function() {
+ p.setup = function () {
p.createCanvas(50, 50, p.WEBGL);
p.gridOutput();
p.circle(0, 0, 20);
@@ -47,20 +47,21 @@ suite('outputs', function() {
let expected =
'Your output is a, 100 by 100 pixels, white canvas containing the following shape:';
- test('should create output as fallback', function() {
- return new Promise(function(resolve, reject) {
+ test('should create output as fallback', function () {
+ return new Promise(function (resolve, reject) {
let actual = '';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
p.textOutput();
p.line(0, 0, 100, 100);
};
- p.draw = function() {
+ p.draw = function () {
if (p.frameCount === 1) {
- actual = document.getElementById('myCanvasIDtextOutput_summary')
- .innerHTML;
+ actual = document.getElementById(
+ 'myCanvasIDtextOutput_summary'
+ ).innerHTML;
if (actual === expected) {
resolve();
} else {
@@ -73,24 +74,25 @@ suite('outputs', function() {
});
});
- test('should create output as label', function() {
- return new Promise(function(resolve, reject) {
+ test('should create output as label', function () {
+ return new Promise(function (resolve, reject) {
let label = '';
let fallback = '';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
};
- p.draw = function() {
+ p.draw = function () {
p.textOutput(p.LABEL);
p.line(0, 0, 100, 100);
if (p.frameCount === 2) {
label = document.getElementById(
'myCanvasIDtextOutputLabel_summary'
).innerHTML;
- fallback = document.getElementById('myCanvasIDtextOutput_summary')
- .innerHTML;
+ fallback = document.getElementById(
+ 'myCanvasIDtextOutput_summary'
+ ).innerHTML;
if (label === expected && fallback === expected) {
resolve();
} else {
@@ -103,22 +105,23 @@ suite('outputs', function() {
});
});
- test('should create text output for arc()', function() {
- return new Promise(function(resolve, reject) {
+ test('should create text output for arc()', function () {
+ return new Promise(function (resolve, reject) {
expected =
'- red arc, at middle, covering 31% of the canvas.
';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
};
- p.draw = function() {
+ p.draw = function () {
p.textOutput();
p.fill(255, 0, 0);
p.arc(50, 50, 80, 80, 0, p.PI + p.QUARTER_PI);
if (p.frameCount === 2) {
- let actual = document.getElementById('myCanvasIDtextOutput_list')
- .innerHTML;
+ let actual = document.getElementById(
+ 'myCanvasIDtextOutput_list'
+ ).innerHTML;
if (actual === expected) {
resolve();
} else {
@@ -131,22 +134,23 @@ suite('outputs', function() {
});
});
- test('should create text output for ellipse()', function() {
- return new Promise(function(resolve, reject) {
+ test('should create text output for ellipse()', function () {
+ return new Promise(function (resolve, reject) {
expected =
'- green circle, at middle, covering 24% of the canvas.
';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
p.textOutput();
p.fill(0, 255, 0);
p.ellipse(56, 46, 55, 55);
};
- p.draw = function() {
+ p.draw = function () {
if (p.frameCount === 1) {
- let actual = document.getElementById('myCanvasIDtextOutput_list')
- .innerHTML;
+ let actual = document.getElementById(
+ 'myCanvasIDtextOutput_list'
+ ).innerHTML;
if (actual === expected) {
resolve();
} else {
@@ -159,22 +163,23 @@ suite('outputs', function() {
});
});
- test('should create text output for triangle()', function() {
- return new Promise(function(resolve, reject) {
+ test('should create text output for triangle()', function () {
+ return new Promise(function (resolve, reject) {
expected =
'- green triangle, at top left, covering 13% of the canvas.
';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
p.textOutput();
p.fill(0, 255, 0);
p.triangle(0, 0, 0, 50, 50, 0);
};
- p.draw = function() {
+ p.draw = function () {
if (p.frameCount === 1) {
- let actual = document.getElementById('myCanvasIDtextOutput_list')
- .innerHTML;
+ let actual = document.getElementById(
+ 'myCanvasIDtextOutput_list'
+ ).innerHTML;
if (actual === expected) {
resolve();
} else {
@@ -188,8 +193,8 @@ suite('outputs', function() {
});
});
- suite('p5.prototype.gridOutput', function() {
- test('should be a function', function() {
+ suite('p5.prototype.gridOutput', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.gridOutput);
assert.typeOf(mockP5Prototype.gridOutput, 'function');
});
@@ -197,20 +202,21 @@ suite('outputs', function() {
let expected =
'white canvas, 100 by 100 pixels, contains 1 shape: 1 square';
- test('should create output as fallback', function() {
- return new Promise(function(resolve, reject) {
+ test('should create output as fallback', function () {
+ return new Promise(function (resolve, reject) {
let actual = '';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
p.gridOutput();
p.rect(0, 0, 100, 100);
};
- p.draw = function() {
+ p.draw = function () {
if (p.frameCount === 1) {
- actual = document.getElementById('myCanvasIDgridOutput_summary')
- .innerHTML;
+ actual = document.getElementById(
+ 'myCanvasIDgridOutput_summary'
+ ).innerHTML;
if (actual === expected) {
resolve();
} else {
@@ -223,24 +229,25 @@ suite('outputs', function() {
});
});
- test('should create output as label', function() {
- return new Promise(function(resolve, reject) {
+ test('should create output as label', function () {
+ return new Promise(function (resolve, reject) {
let label = '';
let fallback = '';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
};
- p.draw = function() {
+ p.draw = function () {
p.gridOutput(p.LABEL);
p.square(0, 0, 100, 100);
if (p.frameCount === 2) {
label = document.getElementById(
'myCanvasIDgridOutputLabel_summary'
).innerHTML;
- fallback = document.getElementById('myCanvasIDgridOutput_summary')
- .innerHTML;
+ fallback = document.getElementById(
+ 'myCanvasIDgridOutput_summary'
+ ).innerHTML;
if (label === expected && fallback === expected) {
resolve();
} else {
@@ -253,21 +260,22 @@ suite('outputs', function() {
});
});
- test('should create text output for quad()', function() {
- return new Promise(function(resolve, reject) {
+ test('should create text output for quad()', function () {
+ return new Promise(function (resolve, reject) {
expected = 'red quadrilateral, location = top left, area = 45 %';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
};
- p.draw = function() {
+ p.draw = function () {
p.gridOutput();
p.fill(255, 0, 0);
p.quad(0, 0, 80, 0, 50, 50, 0, 100);
if (p.frameCount === 2) {
- let actual = document.getElementById('myCanvasIDgridOutputshape0')
- .innerHTML;
+ let actual = document.getElementById(
+ 'myCanvasIDgridOutputshape0'
+ ).innerHTML;
if (actual === expected) {
resolve();
} else {
@@ -280,21 +288,22 @@ suite('outputs', function() {
});
});
- test('should create text output for point()', function() {
- return new Promise(function(resolve, reject) {
+ test('should create text output for point()', function () {
+ return new Promise(function (resolve, reject) {
expected = 'dark fuchsia point, location = bottom right';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
p.gridOutput();
p.stroke('purple');
p.point(85, 75);
};
- p.draw = function() {
+ p.draw = function () {
if (p.frameCount === 1) {
- let actual = document.getElementById('myCanvasIDgridOutputshape0')
- .innerHTML;
+ let actual = document.getElementById(
+ 'myCanvasIDgridOutputshape0'
+ ).innerHTML;
if (actual === expected) {
resolve();
} else {
@@ -307,21 +316,22 @@ suite('outputs', function() {
});
});
- test('should create text output for triangle()', function() {
- return new Promise(function(resolve, reject) {
+ test('should create text output for triangle()', function () {
+ return new Promise(function (resolve, reject) {
expected = 'green triangle, location = top left, area = 13 %';
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
let cnv = p.createCanvas(100, 100);
cnv.id('myCanvasID');
p.gridOutput();
p.fill(0, 255, 0);
p.triangle(0, 0, 0, 50, 50, 0);
};
- p.draw = function() {
+ p.draw = function () {
if (p.frameCount === 1) {
- let actual = document.getElementById('myCanvasIDgridOutputshape0')
- .innerHTML;
+ let actual = document.getElementById(
+ 'myCanvasIDgridOutputshape0'
+ ).innerHTML;
if (actual === expected) {
resolve();
} else {
diff --git a/test/unit/assets/testFilter.js b/test/unit/assets/testFilter.js
index d0aead583b..7a26459c77 100644
--- a/test/unit/assets/testFilter.js
+++ b/test/unit/assets/testFilter.js
@@ -3,4 +3,4 @@ getColor((inputs, canvasContent) => {
const originalColor = getTexture(canvasContent, inputs.texCoord);
// Swap red and green channels, keep blue and alpha
return [originalColor.g, originalColor.r, originalColor.b, originalColor.a];
-});
\ No newline at end of file
+});
diff --git a/test/unit/assets/testMaterial.js b/test/unit/assets/testMaterial.js
index 7c0f32e6b9..6d642922a9 100644
--- a/test/unit/assets/testMaterial.js
+++ b/test/unit/assets/testMaterial.js
@@ -1,9 +1,9 @@
// Test material shader that creates a gradient based on position
-getPixelInputs((inputs) => {
+getPixelInputs(inputs => {
// Create a color gradient based on texture coordinates
const red = inputs.texCoord.x;
const green = inputs.texCoord.y;
const blue = 0.5;
inputs.color = [red, green, blue, 1.0];
return inputs;
-});
\ No newline at end of file
+});
diff --git a/test/unit/color/color_conversion.js b/test/unit/color/color_conversion.js
index 777ddee526..1e3a542e0a 100644
--- a/test/unit/color/color_conversion.js
+++ b/test/unit/color/color_conversion.js
@@ -1,13 +1,13 @@
import p5 from '../../../src/app.js';
-assert.arrayApproximately = function(arr1, arr2, delta) {
+assert.arrayApproximately = function (arr1, arr2, delta) {
assert.equal(arr1.length, arr2.length);
- for(var i = 0; i < arr1.length; i++) {
+ for (var i = 0; i < arr1.length; i++) {
assert.approximately(arr1[i], arr2[i], delta);
}
};
-suite.todo('color/p5.ColorConversion', function() {
+suite.todo('color/p5.ColorConversion', function () {
var rgba = [1, 0, 0.4, 0.8];
var rgbaWithMaxHue = [1, 0, 0, 0.6];
var rgbaWithHighLightness = [0.969, 0.753, 0.122, 0.8];
@@ -18,58 +18,58 @@ suite.todo('color/p5.ColorConversion', function() {
var accuracy = 0.01;
var result;
- suite('rgbaToHSBA', function() {
- test('rgba converts to hsba', function() {
+ suite('rgbaToHSBA', function () {
+ test('rgba converts to hsba', function () {
result = p5.ColorConversion._rgbaToHSBA(rgba);
assert.arrayApproximately(result, hsba, accuracy);
});
});
- suite('hsbaToRGBA', function() {
- test('hsba converts to rgba', function() {
+ suite('hsbaToRGBA', function () {
+ test('hsba converts to rgba', function () {
result = p5.ColorConversion._hsbaToRGBA(hsba);
assert.arrayApproximately(result, rgba, accuracy);
});
- test('handles maximum hue value', function() {
+ test('handles maximum hue value', function () {
result = p5.ColorConversion._hsbaToRGBA(hsbaWithMaxHue);
assert.arrayApproximately(result, rgbaWithMaxHue, accuracy);
});
});
- suite('hslaToRGBA', function() {
- test('hsla converts to rgba', function() {
+ suite('hslaToRGBA', function () {
+ test('hsla converts to rgba', function () {
result = p5.ColorConversion._hslaToRGBA(hsla);
assert.arrayApproximately(result, rgba, accuracy);
});
- test('handles maximum hue value', function() {
+ test('handles maximum hue value', function () {
result = p5.ColorConversion._hslaToRGBA(hslaWithMaxHue);
assert.arrayApproximately(result, rgbaWithMaxHue, accuracy);
});
});
- suite('rgbaToHSLA', function() {
- test('rgba converts to hsla (low lightness)', function() {
+ suite('rgbaToHSLA', function () {
+ test('rgba converts to hsla (low lightness)', function () {
result = p5.ColorConversion._rgbaToHSLA(rgba);
assert.arrayApproximately(result, hsla, accuracy);
});
- test('rgba converts to hsla (high lightness)', function() {
+ test('rgba converts to hsla (high lightness)', function () {
result = p5.ColorConversion._rgbaToHSLA(rgbaWithHighLightness);
assert.arrayApproximately(result, [0.12, 0.93, 0.55, 0.8], accuracy);
});
});
- suite('hslaToHSBA', function() {
- test('hsla converts to hsba', function() {
+ suite('hslaToHSBA', function () {
+ test('hsla converts to hsba', function () {
result = p5.ColorConversion._hslaToHSBA(hsla);
assert.arrayApproximately(result, hsba, accuracy);
});
});
- suite('hsbaToHSLA', function() {
- test('hsba converts to hsla', function() {
+ suite('hsbaToHSLA', function () {
+ test('hsba converts to hsla', function () {
result = p5.ColorConversion._hsbaToHSLA(hsba);
assert.arrayApproximately(result, hsla, accuracy);
});
diff --git a/test/unit/color/creating_reading.js b/test/unit/color/creating_reading.js
index 31d7379802..50ca86501f 100644
--- a/test/unit/color/creating_reading.js
+++ b/test/unit/color/creating_reading.js
@@ -3,8 +3,8 @@ import creatingReading from '../../../src/color/creating_reading';
import setting from '../../../src/color/setting';
import p5Color from '../../../src/color/p5.Color';
-suite('color/CreatingReading', function() {
- beforeAll(async function() {
+suite('color/CreatingReading', function () {
+ beforeAll(async function () {
creatingReading(mockP5, mockP5Prototype);
setting(mockP5, mockP5Prototype);
p5Color(mockP5, mockP5Prototype, {});
@@ -13,51 +13,54 @@ suite('color/CreatingReading', function() {
var fromColor;
var toColor;
- suite.todo('p5.prototype.alpha', function() {
- beforeEach(function() {
+ suite.todo('p5.prototype.alpha', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB);
});
});
- suite.todo('p5.prototype.red, green, blue', function() {
- beforeEach(function() {
+ suite.todo('p5.prototype.red, green, blue', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB);
});
});
- suite.todo('p5.prototype.hue, brightness, lightness, saturation', function() {
- beforeEach(function() {
- mockP5Prototype.colorMode(mockP5Prototype.HSL);
- });
- });
-
- suite('constructor clamping', function() {
- test('should work on multi channels', function() {
+ suite.todo(
+ 'p5.prototype.hue, brightness, lightness, saturation',
+ function () {
+ beforeEach(function () {
+ mockP5Prototype.colorMode(mockP5Prototype.HSL);
+ });
+ }
+ );
+
+ suite('constructor clamping', function () {
+ test('should work on multi channels', function () {
const myColor = mockP5Prototype.color(1000, 1000, 1000, 1000);
assert.deepEqual(myColor.array(), [1, 1, 1, 1]);
});
- test('should work on gray + alpha', function() {
+ test('should work on gray + alpha', function () {
const myColor = mockP5Prototype.color(1000, 1000);
assert.deepEqual(myColor.array(), [1, 1, 1, 1]);
});
- test('should work on gray', function() {
+ test('should work on gray', function () {
const myColor = mockP5Prototype.color(1000);
assert.deepEqual(myColor.array(), [1, 1, 1, 1]);
});
- test('normal values work', function() {
+ test('normal values work', function () {
const myColor = mockP5Prototype.color(255 / 2);
assert.deepEqual(myColor.array(), [0.5, 0.5, 0.5, 1]);
});
});
- suite('p5.prototype.lerpColor', function() {
- beforeEach(function() {
+ suite('p5.prototype.lerpColor', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB);
fromColor = mockP5Prototype.color(218, 165, 32);
toColor = mockP5Prototype.color(72, 61, 139);
});
- test('should correctly get lerp colors in RGB', function() {
+ test('should correctly get lerp colors in RGB', function () {
var interA = mockP5Prototype.lerpColor(fromColor, toColor, 0.33);
var interB = mockP5Prototype.lerpColor(fromColor, toColor, 0.66);
@@ -70,7 +73,7 @@ suite('color/CreatingReading', function() {
assert.closeTo(interB._color.coords[2] * 255, 103, 1);
});
- test('should correctly get lerp colors in HSL', function() {
+ test('should correctly get lerp colors in HSL', function () {
// NOTE: This is equivalent to RGB case so is testing nothing new
mockP5Prototype.colorMode(mockP5Prototype.HSL);
var interA = mockP5Prototype.lerpColor(fromColor, toColor, 0.33);
@@ -85,7 +88,7 @@ suite('color/CreatingReading', function() {
assert.closeTo(interB._color.coords[2], 43, 1);
});
- test('should correctly get lerp colors in HSB', function() {
+ test('should correctly get lerp colors in HSB', function () {
// NOTE: This is equivalent to RGB case so is testing nothing new
mockP5Prototype.colorMode(mockP5Prototype.HSB);
var interA = mockP5Prototype.lerpColor(fromColor, toColor, 0.33);
@@ -100,7 +103,7 @@ suite('color/CreatingReading', function() {
assert.closeTo(interB._color.coords[2], 47, 1);
});
- test.todo('should not extrapolate', function() {
+ test.todo('should not extrapolate', function () {
// NOTE: maybe it should extrapolate
var interA = mockP5Prototype.lerpColor(fromColor, toColor, -0.5);
var interB = mockP5Prototype.lerpColor(fromColor, toColor, 1.5);
@@ -109,14 +112,14 @@ suite('color/CreatingReading', function() {
});
});
- suite('p5.prototype.lerpColor with alpha', function() {
- beforeEach(function() {
+ suite('p5.prototype.lerpColor with alpha', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB);
fromColor = mockP5Prototype.color(218, 165, 32, 49);
toColor = mockP5Prototype.color(72, 61, 139, 200);
});
- test('should correctly get lerp colors in RGB with alpha', function() {
+ test('should correctly get lerp colors in RGB with alpha', function () {
var interA = mockP5Prototype.lerpColor(fromColor, toColor, 0.33);
var interB = mockP5Prototype.lerpColor(fromColor, toColor, 0.66);
@@ -127,11 +130,11 @@ suite('color/CreatingReading', function() {
assert.closeTo(interB._color.coords[0], 0.47, 0.01);
assert.closeTo(interB._color.coords[1], 0.37, 0.01);
- assert.closeTo(interB._color.coords[2], 0.40, 0.01);
+ assert.closeTo(interB._color.coords[2], 0.4, 0.01);
assert.closeTo(interB._color.alpha, 0.58, 0.01);
});
- test('should correctly get lerp colors in HSL with alpha', function() {
+ test('should correctly get lerp colors in HSL with alpha', function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL);
var interA = mockP5Prototype.lerpColor(fromColor, toColor, 0.33);
var interB = mockP5Prototype.lerpColor(fromColor, toColor, 0.66);
@@ -147,7 +150,7 @@ suite('color/CreatingReading', function() {
assert.closeTo(interB._color.alpha, 0.58, 0.01);
});
- test('should correctly get lerp colors in HSB with alpha', function() {
+ test('should correctly get lerp colors in HSB with alpha', function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB);
var interA = mockP5Prototype.lerpColor(fromColor, toColor, 0.33);
var interB = mockP5Prototype.lerpColor(fromColor, toColor, 0.66);
@@ -163,7 +166,7 @@ suite('color/CreatingReading', function() {
assert.closeTo(interB._color.alpha, 0.58, 0.01);
});
- test.todo('should not extrapolate', function() {
+ test.todo('should not extrapolate', function () {
// NOTE: maybe it should extrapolate
var interA = mockP5Prototype.lerpColor(fromColor, toColor, -0.5);
var interB = mockP5Prototype.lerpColor(fromColor, toColor, 1.5);
diff --git a/test/unit/color/hsb.js b/test/unit/color/hsb.js
index 7761c9594e..e536cc78d2 100644
--- a/test/unit/color/hsb.js
+++ b/test/unit/color/hsb.js
@@ -15,26 +15,26 @@
import HSBSpace from '../../../src/color/color_spaces/hsb.js';
-suite('color/HSB Color Space', function() {
+suite('color/HSB Color Space', function () {
const accuracy = 0.01;
- suite('HSB to RGB conversion (toBase)', function() {
- suite('Primary Colors', function() {
- test('pure red: HSB(0, 100, 100) → RGB(1, 0, 0)', function() {
+ suite('HSB to RGB conversion (toBase)', function () {
+ suite('Primary Colors', function () {
+ test('pure red: HSB(0, 100, 100) → RGB(1, 0, 0)', function () {
const result = HSBSpace.toBase([0, 100, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});
- test('pure green: HSB(120, 100, 100) → RGB(0, 1, 0)', function() {
+ test('pure green: HSB(120, 100, 100) → RGB(0, 1, 0)', function () {
const result = HSBSpace.toBase([120, 100, 100]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 1, accuracy);
assert.approximately(result[2], 0, accuracy);
});
- test('pure blue: HSB(240, 100, 100) → RGB(0, 0, 1)', function() {
+ test('pure blue: HSB(240, 100, 100) → RGB(0, 0, 1)', function () {
const result = HSBSpace.toBase([240, 100, 100]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
@@ -42,22 +42,22 @@ suite('color/HSB Color Space', function() {
});
});
- suite('Secondary Colors', function() {
- test('yellow: HSB(60, 100, 100) → RGB(1, 1, 0)', function() {
+ suite('Secondary Colors', function () {
+ test('yellow: HSB(60, 100, 100) → RGB(1, 1, 0)', function () {
const result = HSBSpace.toBase([60, 100, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 1, accuracy);
assert.approximately(result[2], 0, accuracy);
});
- test('cyan: HSB(180, 100, 100) → RGB(0, 1, 1)', function() {
+ test('cyan: HSB(180, 100, 100) → RGB(0, 1, 1)', function () {
const result = HSBSpace.toBase([180, 100, 100]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 1, accuracy);
assert.approximately(result[2], 1, accuracy);
});
- test('magenta: HSB(300, 100, 100) → RGB(1, 0, 1)', function() {
+ test('magenta: HSB(300, 100, 100) → RGB(1, 0, 1)', function () {
const result = HSBSpace.toBase([300, 100, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 0, accuracy);
@@ -65,29 +65,29 @@ suite('color/HSB Color Space', function() {
});
});
- suite('Grayscale (Saturation = 0)', function() {
- test('white: HSB(0, 0, 100) → RGB(1, 1, 1)', function() {
+ suite('Grayscale (Saturation = 0)', function () {
+ test('white: HSB(0, 0, 100) → RGB(1, 1, 1)', function () {
const result = HSBSpace.toBase([0, 0, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 1, accuracy);
assert.approximately(result[2], 1, accuracy);
});
- test('black: HSB(0, 0, 0) → RGB(0, 0, 0)', function() {
+ test('black: HSB(0, 0, 0) → RGB(0, 0, 0)', function () {
const result = HSBSpace.toBase([0, 0, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});
- test('mid gray: HSB(0, 0, 50) → RGB(0.5, 0.5, 0.5)', function() {
+ test('mid gray: HSB(0, 0, 50) → RGB(0.5, 0.5, 0.5)', function () {
const result = HSBSpace.toBase([0, 0, 50]);
assert.approximately(result[0], 0.5, accuracy);
assert.approximately(result[1], 0.5, accuracy);
assert.approximately(result[2], 0.5, accuracy);
});
- test('grayscale ignores hue: HSB(180, 0, 50) → RGB(0.5, 0.5, 0.5)', function() {
+ test('grayscale ignores hue: HSB(180, 0, 50) → RGB(0.5, 0.5, 0.5)', function () {
const result = HSBSpace.toBase([180, 0, 50]);
assert.approximately(result[0], 0.5, accuracy);
assert.approximately(result[1], 0.5, accuracy);
@@ -95,15 +95,15 @@ suite('color/HSB Color Space', function() {
});
});
- suite('Partial Saturation', function() {
- test('50% saturation red: HSB(0, 50, 100) → RGB(1, 0.5, 0.5)', function() {
+ suite('Partial Saturation', function () {
+ test('50% saturation red: HSB(0, 50, 100) → RGB(1, 0.5, 0.5)', function () {
const result = HSBSpace.toBase([0, 50, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 0.5, accuracy);
assert.approximately(result[2], 0.5, accuracy);
});
- test('50% brightness red: HSB(0, 100, 50) → RGB(0.5, 0, 0)', function() {
+ test('50% brightness red: HSB(0, 100, 50) → RGB(0.5, 0, 0)', function () {
const result = HSBSpace.toBase([0, 100, 50]);
assert.approximately(result[0], 0.5, accuracy);
assert.approximately(result[1], 0, accuracy);
@@ -111,15 +111,15 @@ suite('color/HSB Color Space', function() {
});
});
- suite('Edge Cases', function() {
- test('hue at 360 should be same as 0 (red)', function() {
+ suite('Edge Cases', function () {
+ test('hue at 360 should be same as 0 (red)', function () {
const result = HSBSpace.toBase([360, 100, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});
- test('brightness 0 produces black regardless of hue/saturation', function() {
+ test('brightness 0 produces black regardless of hue/saturation', function () {
const result = HSBSpace.toBase([120, 100, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
@@ -128,23 +128,23 @@ suite('color/HSB Color Space', function() {
});
});
- suite('RGB to HSB conversion (fromBase)', function() {
- suite('Primary Colors', function() {
- test('pure red: RGB(1, 0, 0) → HSB(0, 100, 100)', function() {
+ suite('RGB to HSB conversion (fromBase)', function () {
+ suite('Primary Colors', function () {
+ test('pure red: RGB(1, 0, 0) → HSB(0, 100, 100)', function () {
const result = HSBSpace.fromBase([1, 0, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});
- test('pure green: RGB(0, 1, 0) → HSB(120, 100, 100)', function() {
+ test('pure green: RGB(0, 1, 0) → HSB(120, 100, 100)', function () {
const result = HSBSpace.fromBase([0, 1, 0]);
assert.approximately(result[0], 120, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});
- test('pure blue: RGB(0, 0, 1) → HSB(240, 100, 100)', function() {
+ test('pure blue: RGB(0, 0, 1) → HSB(240, 100, 100)', function () {
const result = HSBSpace.fromBase([0, 0, 1]);
assert.approximately(result[0], 240, accuracy);
assert.approximately(result[1], 100, accuracy);
@@ -152,22 +152,22 @@ suite('color/HSB Color Space', function() {
});
});
- suite('Secondary Colors', function() {
- test('yellow: RGB(1, 1, 0) → HSB(60, 100, 100)', function() {
+ suite('Secondary Colors', function () {
+ test('yellow: RGB(1, 1, 0) → HSB(60, 100, 100)', function () {
const result = HSBSpace.fromBase([1, 1, 0]);
assert.approximately(result[0], 60, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});
- test('cyan: RGB(0, 1, 1) → HSB(180, 100, 100)', function() {
+ test('cyan: RGB(0, 1, 1) → HSB(180, 100, 100)', function () {
const result = HSBSpace.fromBase([0, 1, 1]);
assert.approximately(result[0], 180, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});
- test('magenta: RGB(1, 0, 1) → HSB(300, 100, 100)', function() {
+ test('magenta: RGB(1, 0, 1) → HSB(300, 100, 100)', function () {
const result = HSBSpace.fromBase([1, 0, 1]);
assert.approximately(result[0], 300, accuracy);
assert.approximately(result[1], 100, accuracy);
@@ -175,22 +175,22 @@ suite('color/HSB Color Space', function() {
});
});
- suite('Grayscale', function() {
- test('white: RGB(1, 1, 1) → HSB(0, 0, 100)', function() {
+ suite('Grayscale', function () {
+ test('white: RGB(1, 1, 1) → HSB(0, 0, 100)', function () {
const result = HSBSpace.fromBase([1, 1, 1]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 100, accuracy);
});
- test('black: RGB(0, 0, 0) → HSB(0, 0, 0)', function() {
+ test('black: RGB(0, 0, 0) → HSB(0, 0, 0)', function () {
const result = HSBSpace.fromBase([0, 0, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});
- test('mid gray: RGB(0.5, 0.5, 0.5) → HSB(0, 0, 50)', function() {
+ test('mid gray: RGB(0.5, 0.5, 0.5) → HSB(0, 0, 50)', function () {
const result = HSBSpace.fromBase([0.5, 0.5, 0.5]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
@@ -198,15 +198,15 @@ suite('color/HSB Color Space', function() {
});
});
- suite('Partial Values', function() {
- test('dark red: RGB(0.5, 0, 0) → HSB(0, 100, 50)', function() {
+ suite('Partial Values', function () {
+ test('dark red: RGB(0.5, 0, 0) → HSB(0, 100, 50)', function () {
const result = HSBSpace.fromBase([0.5, 0, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 50, accuracy);
});
- test('light red (pink): RGB(1, 0.5, 0.5) → HSB(0, 50, 100)', function() {
+ test('light red (pink): RGB(1, 0.5, 0.5) → HSB(0, 50, 100)', function () {
const result = HSBSpace.fromBase([1, 0.5, 0.5]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 50, accuracy);
@@ -215,8 +215,8 @@ suite('color/HSB Color Space', function() {
});
});
- suite('Round-trip conversion', function() {
- test('HSB → RGB → HSB should preserve values', function() {
+ suite('Round-trip conversion', function () {
+ test('HSB → RGB → HSB should preserve values', function () {
const originalHSB = [210, 75, 80];
const rgb = HSBSpace.toBase(originalHSB);
const resultHSB = HSBSpace.fromBase(rgb);
@@ -226,7 +226,7 @@ suite('color/HSB Color Space', function() {
assert.approximately(resultHSB[2], originalHSB[2], 0.1);
});
- test('RGB → HSB → RGB should preserve values', function() {
+ test('RGB → HSB → RGB should preserve values', function () {
const originalRGB = [0.7, 0.3, 0.5];
const hsb = HSBSpace.fromBase(originalRGB);
const resultRGB = HSBSpace.toBase(hsb);
diff --git a/test/unit/color/p5.Color.js b/test/unit/color/p5.Color.js
index 73604d595a..5021a53f18 100644
--- a/test/unit/color/p5.Color.js
+++ b/test/unit/color/p5.Color.js
@@ -4,7 +4,7 @@ import setting from '../../../src/color/setting';
import { Color } from '../../../src/color/p5.Color';
import p5Color from '../../../src/color/p5.Color';
-suite('p5.Color', function() {
+suite('p5.Color', function () {
beforeAll(() => {
creatingReading(mockP5, mockP5Prototype);
setting(mockP5, mockP5Prototype);
@@ -13,28 +13,28 @@ suite('p5.Color', function() {
var c;
- suite('p5.prototype.color(r,g,b)', function() {
- beforeEach(function() {
+ suite('p5.prototype.color(r,g,b)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color(255, 0, 102);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 1);
});
});
- suite('p5.prototype.color(r,g,b,a)', function() {
- beforeEach(function() {
+ suite('p5.prototype.color(r,g,b,a)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color(255, 0, 102, 204);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 0.8);
});
@@ -42,131 +42,134 @@ suite('p5.Color', function() {
// NOTE: suite for all signatures
- suite('p5.prototype.color(string)', function(){
- suite('#rgb', function(){
- beforeEach(function() {
+ suite('p5.prototype.color(string)', function () {
+ suite('#rgb', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('#f06');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 1);
});
});
- suite('#rgba', function(){
- beforeEach(function() {
+ suite('#rgba', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('#f066');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 0.4);
});
});
- suite('#rrggbb', function(){
- beforeEach(function() {
+ suite('#rrggbb', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('#ff0066');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 1);
});
});
- suite('#rrggbbaa', function(){
- beforeEach(function() {
+ suite('#rrggbbaa', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('#f01dab1e');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
- assert.deepEqual(c._color.coords, [240, 29, 171].map(c => c/255));
- assert.equal(c._color.alpha, 30/255);
+ test('should correctly set RGBA property', function () {
+ assert.deepEqual(
+ c._color.coords,
+ [240, 29, 171].map(c => c / 255)
+ );
+ assert.equal(c._color.alpha, 30 / 255);
});
});
- suite('rgb(r,g,b)', function(){
- beforeEach(function() {
+ suite('rgb(r,g,b)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('rgb(255,0,102)');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 1);
});
});
- suite('rgb(r%,g%,b%)', function(){
- beforeEach(function() {
+ suite('rgb(r%,g%,b%)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('rgb(100%, 0%, 40%)');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 1);
});
});
- suite('rgba(r,g,b,a)', function(){
- beforeEach(function() {
+ suite('rgba(r,g,b,a)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('rgba(255,0,102,0.8)');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 0.8);
});
});
- suite('rgba(r%,g%,b%,a)', function(){
- beforeEach(function() {
+ suite('rgba(r%,g%,b%,a)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('rgba(100.0%,0.0%,40%,0.8)');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 0.8);
});
});
- suite('hsl(h, s%, l%)', function(){
- beforeEach(function() {
+ suite('hsl(h, s%, l%)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('hsl(336, 100%, 50%)');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
// NOTE: 0.5.2 of color.js uses `new Number` which is corrected in future version
// assert.deepEqual(c.color.coords, [336, 100, 50]);
assert.equal(+c._color.coords[0], 336);
@@ -176,14 +179,14 @@ suite('p5.Color', function() {
});
});
- suite('hsla(h, s%, l%, a)', function() {
- beforeEach(function() {
+ suite('hsla(h, s%, l%, a)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('hsla(336, 100%, 50%, 0.8)');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
// NOTE: 0.5.2 of color.js uses `new Number` which is corrected in future version
// assert.deepEqual(c.color.coords, [336, 100, 50]);
assert.equal(+c._color.coords[0], 336);
@@ -193,14 +196,14 @@ suite('p5.Color', function() {
});
});
- suite('hsb(h, s%, b%)', function() {
- beforeEach(function() {
+ suite('hsb(h, s%, b%)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('hsb(336, 100%, 100%)');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
// NOTE: 0.5.2 of color.js uses `new Number` which is corrected in future version
// assert.deepEqual(c.color.coords, [336, 100, 100]);
assert.equal(+c._color.coords[0], 336);
@@ -210,14 +213,14 @@ suite('p5.Color', function() {
});
});
- suite('hsba(h, s%, b%, a)', function() {
- beforeEach(function() {
+ suite('hsba(h, s%, b%, a)', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('hsba(336, 100%, 100%, 0.8)');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
// NOTE: 0.5.2 of color.js uses `new Number` which is corrected in future version
// assert.deepEqual(c.color.coords, [336, 100, 50]);
assert.equal(+c._color.coords[0], 336);
@@ -227,17 +230,20 @@ suite('p5.Color', function() {
});
});
- suite('named colors', function() {
- beforeEach(function() {
+ suite('named colors', function () {
+ beforeEach(function () {
c = mockP5Prototype.color('papayawhip');
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
- assert.deepEqual(c._color.coords, [255, 239, 213].map(c => c/255));
+ test('should correctly set RGBA property', function () {
+ assert.deepEqual(
+ c._color.coords,
+ [255, 239, 213].map(c => c / 255)
+ );
assert.equal(c._color.alpha, 1);
});
});
@@ -246,121 +252,121 @@ suite('p5.Color', function() {
suite.todo('invalid string');
});
- suite('p5.prototype.color([])', function() {
- beforeEach(function() {
+ suite('p5.prototype.color([])', function () {
+ beforeEach(function () {
c = mockP5Prototype.color([255, 0, 102]);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 1);
});
});
// color level setters
- suite('in default mode', function() {
- test('can be modified with alpha setter', function() {
+ suite('in default mode', function () {
+ test('can be modified with alpha setter', function () {
let cc = mockP5Prototype.color(255, 0, 102, 204);
assert.deepEqual(cc._color.coords, [1, 0, 0.4]);
assert.equal(cc._color.alpha, 0.8);
- cc.setAlpha(98/255);
+ cc.setAlpha(98 / 255);
assert.deepEqual(cc._color.coords, [1, 0, 0.4]);
- assert.equal(cc._color.alpha, 98/255);
+ assert.equal(cc._color.alpha, 98 / 255);
});
- test('can be modified with rgb setters', function() {
+ test('can be modified with rgb setters', function () {
var cc = mockP5Prototype.color(255, 0, 102, 204);
assert.deepEqual(cc._color.coords, [1, 0, 0.4]);
assert.equal(cc._color.alpha, 0.8);
- cc.setRed(98/255);
- assert.deepEqual(cc._color.coords, [98/255, 0, 0.4]);
+ cc.setRed(98 / 255);
+ assert.deepEqual(cc._color.coords, [98 / 255, 0, 0.4]);
assert.equal(cc._color.alpha, 0.8);
- cc.setGreen(44/255);
- assert.deepEqual(cc._color.coords, [98/255, 44/255, 0.4]);
+ cc.setGreen(44 / 255);
+ assert.deepEqual(cc._color.coords, [98 / 255, 44 / 255, 0.4]);
assert.equal(cc._color.alpha, 0.8);
- cc.setBlue(244/255);
- assert.deepEqual(cc._color.coords, [98/255, 44/255, 244/255]);
+ cc.setBlue(244 / 255);
+ assert.deepEqual(cc._color.coords, [98 / 255, 44 / 255, 244 / 255]);
assert.equal(cc._color.alpha, 0.8);
});
});
// Color Mode
- suite('p5.Color in RGB mode with custom range', function() {
- beforeEach(function() {
+ suite('p5.Color in RGB mode with custom range', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 1);
c = mockP5Prototype.color(1, 0, 0.4, 0.8);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly get RGBA property', function() {
+ test('should correctly get RGBA property', function () {
assert.equal(c._getRed(), 1);
assert.equal(c._getGreen(), 0);
assert.equal(c._getBlue(), 0.4);
assert.approximately(c._getAlpha(), 0.8, 0.05);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), '#ff0066cc');
});
- test('should correctly get RGBA property after overwrite', function() {
+ test('should correctly get RGBA property after overwrite', function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 255, 255, 255, 255);
- assert.equal(c._getRed(), 255/255);
- assert.equal(c._getGreen(), 0/255);
- assert.equal(c._getBlue(), 102/255);
- assert.equal(c._getAlpha(), 204/255);
+ assert.equal(c._getRed(), 255 / 255);
+ assert.equal(c._getGreen(), 0 / 255);
+ assert.equal(c._getBlue(), 102 / 255);
+ assert.equal(c._getAlpha(), 204 / 255);
});
});
- suite('p5.Color in HSL mode', function() {
- beforeEach(function() {
+ suite('p5.Color in HSL mode', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL);
c = mockP5Prototype.color(336, 100, 50);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [336, 100, 50]);
assert.equal(c._color.alpha, 1);
});
- test('can be modified with alpha setter', function() {
+ test('can be modified with alpha setter', function () {
let cc = mockP5Prototype.color(336, 100, 50);
cc.setAlpha(0.73);
assert.deepEqual(cc._color.coords, [336, 100, 50]);
assert.equal(cc._color.alpha, 0.73);
});
- test('can be modified with rgb setters', function() {
+ test('can be modified with rgb setters', function () {
let cc = mockP5Prototype.color(336, 100, 50);
assert.deepEqual(cc._color.coords, [336, 100, 50]);
assert.equal(cc._color.alpha, 1);
// TODO: separately check these values are correct (not in test here)
- cc.setRed(98/255);
+ cc.setRed(98 / 255);
assert.closeTo(cc._color.coords[0], 297, 1);
assert.closeTo(cc._color.coords[1], 100, 1);
assert.closeTo(cc._color.coords[2], 20, 1);
assert.equal(cc._color.alpha, 1);
- cc.setGreen(44/255);
+ cc.setGreen(44 / 255);
assert.closeTo(cc._color.coords[0], 295, 1);
assert.closeTo(cc._color.coords[1], 39, 1);
assert.closeTo(cc._color.coords[2], 28, 1);
assert.equal(cc._color.alpha, 1);
- cc.setBlue(244/255);
+ cc.setBlue(244 / 255);
assert.closeTo(cc._color.coords[0], 256, 1);
assert.closeTo(cc._color.coords[1], 90, 1);
assert.closeTo(cc._color.coords[2], 56, 1);
@@ -368,22 +374,22 @@ suite('p5.Color', function() {
});
});
- suite('p5.Color in HSL mode with Alpha', function() {
- beforeEach(function() {
+ suite('p5.Color in HSL mode with Alpha', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL);
c = mockP5Prototype.color(336, 100, 50, 0.8);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [336, 100, 50]);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly get hue/saturation/lightness/alpha', function() {
+ test('should correctly get hue/saturation/lightness/alpha', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getLightness(), 50, 0.5);
@@ -391,31 +397,31 @@ suite('p5.Color', function() {
});
});
- suite('p5.Color in HSL mode with custom range', function() {
- beforeEach(function() {
+ suite('p5.Color in HSL mode with custom range', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL, 100, 200, 300, 10);
c = mockP5Prototype.color(93.33, 200, 150, 8);
});
- test('should correctly get HSLA property', function() {
+ test('should correctly get HSLA property', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getLightness(), 50, 0.5);
assert.approximately(c._getAlpha(), 0.8, 0.5);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
assert.closeTo(c._color.coords[0], 336, 1);
assert.equal(c._color.coords[1], 100);
assert.equal(c._color.coords[2], 50);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), 'hsl(335.99 100% 50% / 0.8)');
});
- test('can be modified with alpha setter', function() {
+ test('can be modified with alpha setter', function () {
let cc = mockP5Prototype.color(93.33, 200, 150, 8);
cc.setAlpha(0.73);
assert.closeTo(cc._color.coords[0], 336, 1);
@@ -424,26 +430,26 @@ suite('p5.Color', function() {
assert.equal(cc._color.alpha, 0.73);
});
- test('can be modified with rgb setters', function() {
+ test('can be modified with rgb setters', function () {
let cc = mockP5Prototype.color(93.33, 200, 150, 8);
assert.closeTo(c._color.coords[0], 336, 1);
assert.equal(c._color.coords[1], 100);
assert.equal(c._color.coords[2], 50);
assert.equal(c._color.alpha, 0.8);
- cc.setRed(98/255);
+ cc.setRed(98 / 255);
assert.closeTo(cc._color.coords[0], 297, 1);
assert.closeTo(cc._color.coords[1], 100, 1);
assert.closeTo(cc._color.coords[2], 20, 1);
assert.equal(cc._color.alpha, 0.8);
- cc.setGreen(44/255);
+ cc.setGreen(44 / 255);
assert.closeTo(cc._color.coords[0], 295, 1);
assert.closeTo(cc._color.coords[1], 39, 1);
assert.closeTo(cc._color.coords[2], 28, 1);
assert.equal(cc._color.alpha, 0.8);
- cc.setBlue(244/255);
+ cc.setBlue(244 / 255);
assert.closeTo(cc._color.coords[0], 256, 1);
assert.closeTo(cc._color.coords[1], 90, 1);
assert.closeTo(cc._color.coords[2], 56, 1);
@@ -451,44 +457,44 @@ suite('p5.Color', function() {
});
});
- suite('p5.Color in HSL mode with RGB string', function() {
+ suite('p5.Color in HSL mode with RGB string', function () {
// NOTE: will still create a sRGB color in this case
- beforeEach(function() {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL, 360, 100, 100, 1);
c = mockP5Prototype.color('rgba(255, 0, 102, 0.8)');
});
- test.todo('should correctly get HSLA property', function() {
+ test.todo('should correctly get HSLA property', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getLightness(), 50, 0.5);
assert.approximately(c._getAlpha(), 0.8, 0.05);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), 'rgba(255, 0, 102, 0.8)');
});
});
- suite('p5.Color in HSL mode with HSL string', function() {
- beforeEach(function() {
+ suite('p5.Color in HSL mode with HSL string', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL, 360, 100, 100, 1);
c = mockP5Prototype.color('hsla(336, 100%, 50%, 0.8)');
});
- test.todo('should correctly get HSLA property', function() {
+ test.todo('should correctly get HSLA property', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getLightness(), 50, 0.5);
assert.approximately(c._getAlpha(), 0.8, 0.05);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
// NOTE: 0.5.2 of color.js uses `new Number` which is corrected in future version
// assert.deepEqual(c.color.coords, [336, 100, 50]);
assert.equal(+c._color.coords[0], 336);
@@ -497,25 +503,25 @@ suite('p5.Color', function() {
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), 'hsla(336, 100%, 50%, 0.8)');
});
});
- suite('p5.Color in HSL mode with HSB string', function() {
- beforeEach(function() {
+ suite('p5.Color in HSL mode with HSB string', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL, 360, 100, 100, 1);
c = mockP5Prototype.color('hsba(336, 100%, 100%, 0.8)');
});
- test.todo('should correctly get HSLA property', function() {
+ test.todo('should correctly get HSLA property', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getLightness(), 50, 0.5);
assert.approximately(c._getAlpha(), 0.8, 0.05);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
// NOTE: 0.5.2 of color.js uses `new Number` which is corrected in future version
// assert.deepEqual(c.color.coords, [336, 100, 50]);
assert.equal(+c._color.coords[0], 336);
@@ -524,51 +530,51 @@ suite('p5.Color', function() {
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), 'hsba(336, 100%, 100%, 0.8)');
});
});
- suite('p5.Color in HSB mode', function() {
- beforeEach(function() {
+ suite('p5.Color in HSB mode', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB);
c = mockP5Prototype.color(336, 100, 100);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [336, 100, 100]);
assert.equal(c._color.alpha, 1);
});
- test('can be modified with alpha setter', function() {
+ test('can be modified with alpha setter', function () {
var cc = mockP5Prototype.color(336, 100, 100);
cc.setAlpha(0.73);
assert.deepEqual(cc._color.coords, [336, 100, 100]);
assert.equal(cc._color.alpha, 0.73);
});
- test('can be modified with rgb setters', function() {
+ test('can be modified with rgb setters', function () {
var cc = mockP5Prototype.color(336, 100, 100);
assert.deepEqual(cc._color.coords, [336, 100, 100]);
assert.equal(cc._color.alpha, 1);
- cc.setRed(98/255);
+ cc.setRed(98 / 255);
assert.closeTo(cc._color.coords[0], 297, 1);
assert.closeTo(cc._color.coords[1], 100, 1);
assert.closeTo(cc._color.coords[2], 40, 1);
assert.equal(cc._color.alpha, 1);
- cc.setGreen(44/255);
+ cc.setGreen(44 / 255);
assert.closeTo(cc._color.coords[0], 295, 1);
assert.closeTo(cc._color.coords[1], 56, 1);
assert.closeTo(cc._color.coords[2], 40, 1);
assert.equal(cc._color.alpha, 1);
- cc.setBlue(244/255);
+ cc.setBlue(244 / 255);
assert.closeTo(cc._color.coords[0], 256, 1);
assert.closeTo(cc._color.coords[1], 81, 1);
assert.closeTo(cc._color.coords[2], 95, 1);
@@ -576,20 +582,20 @@ suite('p5.Color', function() {
});
});
- suite('p5.Color in HSB mode with Alpha', function() {
- beforeEach(function() {
+ suite('p5.Color in HSB mode with Alpha', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB);
c = mockP5Prototype.color(336, 100, 100, 0.8);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGBA property', function() {
+ test('should correctly set RGBA property', function () {
assert.deepEqual(c._color.coords, [336, 100, 100]);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly get hue/saturation/brightness/alpha', function() {
+ test('should correctly get hue/saturation/brightness/alpha', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getBrightness(), 100, 0.5);
@@ -597,210 +603,210 @@ suite('p5.Color', function() {
});
});
- suite('p5.Color in HSB mode with custom range', function() {
- beforeEach(function() {
+ suite('p5.Color in HSB mode with custom range', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB, 100, 200, 300, 10);
c = mockP5Prototype.color(93.33, 200, 300, 8);
});
- test('should correctly get HSBA property', function() {
+ test('should correctly get HSBA property', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getBrightness(), 100, 0.5);
assert.approximately(c._getAlpha(), 0.8, 0.5);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
assert.closeTo(c._color.coords[0], 336, 1);
assert.equal(c._color.coords[1], 100);
assert.equal(c._color.coords[2], 100);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), 'rgb(100% 0% 40.02% / 0.8)');
});
});
- suite('p5.Color in HSB mode with RGB string', function() {
- beforeEach(function() {
+ suite('p5.Color in HSB mode with RGB string', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB, 360, 100, 100, 1);
c = mockP5Prototype.color('rgba(255, 0, 102, 0.8)');
});
- test.todo('should correctly get HSBA property', function() {
+ test.todo('should correctly get HSBA property', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getBrightness(), 100, 0.5);
assert.approximately(c._getAlpha(), 0.8, 0.05);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
assert.deepEqual(c._color.coords, [1, 0, 0.4]);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), 'rgba(255, 0, 102, 0.8)');
});
});
- suite('p5.Color in HSB mode with HSB string', function() {
- beforeEach(function() {
+ suite('p5.Color in HSB mode with HSB string', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB, 360, 100, 100, 1);
c = mockP5Prototype.color('hsba(336, 100%, 100%, 0.8)');
});
- test('should correctly get HSBA property', function() {
+ test('should correctly get HSBA property', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getBrightness(), 100, 0.5);
assert.approximately(c._getAlpha(), 0.8, 0.05);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
assert.equal(+c._color.coords[0], 336);
assert.equal(c._color.coords[1], 100);
assert.equal(c._color.coords[2], 100);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), 'hsba(336, 100%, 100%, 0.8)');
});
});
- suite('p5.Color in HSB mode with HSL string', function() {
- beforeEach(function() {
+ suite('p5.Color in HSB mode with HSL string', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB, 360, 100, 100, 1);
c = mockP5Prototype.color('hsla(336, 100%, 50%, 0.8)');
});
- test.todo('should correctly get HSBA property', function() {
+ test.todo('should correctly get HSBA property', function () {
assert.approximately(c._getHue(), 336, 0.5);
assert.approximately(c._getSaturation(), 100, 0.5);
assert.approximately(c._getBrightness(), 100, 0.5);
assert.approximately(c._getAlpha(), 0.8, 0.05);
});
- test('should correctly convert to RGBA', function() {
+ test('should correctly convert to RGBA', function () {
assert.equal(+c._color.coords[0], 336);
assert.equal(c._color.coords[1], 100);
assert.equal(c._color.coords[2], 50);
assert.equal(c._color.alpha, 0.8);
});
- test('should correctly render color string', function() {
+ test('should correctly render color string', function () {
assert.equal(c.toString(), 'hsla(336, 100%, 50%, 0.8)');
});
});
- suite('p5.Color in RGB mode with grayscale value', function() {
- beforeEach(function() {
+ suite('p5.Color in RGB mode with grayscale value', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB);
c = mockP5Prototype.color(100);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGB levels', function() {
- assert.deepEqual(c._color.coords, [100/255, 100/255, 100/255]);
+ test('should correctly set RGB levels', function () {
+ assert.deepEqual(c._color.coords, [100 / 255, 100 / 255, 100 / 255]);
assert.equal(c._color.alpha, 1);
});
});
- suite('p5.Color in RGB mode with grayscale value and alpha', function() {
- beforeEach(function() {
+ suite('p5.Color in RGB mode with grayscale value and alpha', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB);
c = mockP5Prototype.color(100, 70);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGB levels', function() {
- assert.deepEqual(c._color.coords, [100/255, 100/255, 100/255]);
- assert.equal(c._color.alpha, 70/255);
+ test('should correctly set RGB levels', function () {
+ assert.deepEqual(c._color.coords, [100 / 255, 100 / 255, 100 / 255]);
+ assert.equal(c._color.alpha, 70 / 255);
});
});
- suite('p5.Color in HSB mode with grayscale value', function() {
- beforeEach(function() {
+ suite('p5.Color in HSB mode with grayscale value', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB);
c = mockP5Prototype.color(39.3);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set color coordinates', function() {
+ test('should correctly set color coordinates', function () {
assert.deepEqual(c._color.coords, [0, 0, 39.3]);
assert.equal(c._color.alpha, 1);
});
});
- suite('p5.Color in HSB mode with grayscale value and alpha', function() {
- beforeEach(function() {
+ suite('p5.Color in HSB mode with grayscale value and alpha', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSB);
c = mockP5Prototype.color(39.3, 0.275);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGB levels', function() {
+ test('should correctly set RGB levels', function () {
assert.deepEqual(c._color.coords, [0, 0, 39.3]);
assert.equal(c._color.alpha, 0.275);
});
});
- suite('p5.Color in HSL mode with grayscale value', function() {
- beforeEach(function() {
+ suite('p5.Color in HSL mode with grayscale value', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL);
c = mockP5Prototype.color(39.3);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGB levels', function() {
+ test('should correctly set RGB levels', function () {
assert.deepEqual(c._color.coords, [0, 0, 39.3]);
assert.equal(c._color.alpha, 1);
});
});
- suite('p5.Color in HSL mode with grayscale value and alpha', function() {
- beforeEach(function() {
+ suite('p5.Color in HSL mode with grayscale value and alpha', function () {
+ beforeEach(function () {
mockP5Prototype.colorMode(mockP5Prototype.HSL);
c = mockP5Prototype.color(39.3, 0.275);
});
- test('should create instance of p5.Color', function() {
+ test('should create instance of p5.Color', function () {
assert.instanceOf(c, Color);
});
- test('should correctly set RGB levels', function() {
+ test('should correctly set RGB levels', function () {
assert.deepEqual(c._color.coords, [0, 0, 39.3]);
assert.equal(c._color.alpha, 0.275);
});
});
- suite('p5.Color.prototype.toString', function() {
- suite('#rrggbb format', function() {
- test('should expand short hex (#rgb) to full 6-digit format', function() {
+ suite('p5.Color.prototype.toString', function () {
+ suite('#rrggbb format', function () {
+ test('should expand short hex (#rgb) to full 6-digit format', function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 255, 255, 255, 255);
const c = mockP5Prototype.color('#f06');
const result = c.toString('#rrggbb');
assert.equal(result, '#ff0066');
});
- test('should truncate hex with alpha (#rrggbbaa) to 6 digits', function() {
+ test('should truncate hex with alpha (#rrggbbaa) to 6 digits', function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 255, 255, 255, 255);
const c = mockP5Prototype.color(255, 0, 102, 128);
const result = c.toString('#rrggbb');
@@ -808,7 +814,7 @@ suite('p5.Color', function() {
assert.equal(result, '#ff0066');
});
- test('should output lowercase hex string', function() {
+ test('should output lowercase hex string', function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 255, 255, 255, 255);
const c = mockP5Prototype.color(255, 170, 187);
const result = c.toString('#rrggbb');
@@ -816,28 +822,28 @@ suite('p5.Color', function() {
assert.equal(result, '#ffaabb');
});
- test('should correctly format standard RGB colors', function() {
+ test('should correctly format standard RGB colors', function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 255, 255, 255, 255);
const c = mockP5Prototype.color(255, 0, 102);
const result = c.toString('#rrggbb');
assert.equal(result, '#ff0066');
});
- test('should correctly format black color', function() {
+ test('should correctly format black color', function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 255, 255, 255, 255);
const c = mockP5Prototype.color(0, 0, 0);
const result = c.toString('#rrggbb');
assert.equal(result, '#000000');
});
- test('should correctly format white color', function() {
+ test('should correctly format white color', function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 255, 255, 255, 255);
const c = mockP5Prototype.color(255, 255, 255);
const result = c.toString('#rrggbb');
assert.equal(result, '#ffffff');
});
- test('should handle colors created from 6-digit hex string', function() {
+ test('should handle colors created from 6-digit hex string', function () {
mockP5Prototype.colorMode(mockP5Prototype.RGB, 255, 255, 255, 255);
const c = mockP5Prototype.color('#9932cc');
const result = c.toString('#rrggbb');
diff --git a/test/unit/color/setting.js b/test/unit/color/setting.js
index ba70afa7a0..eaa56f8803 100644
--- a/test/unit/color/setting.js
+++ b/test/unit/color/setting.js
@@ -7,7 +7,7 @@ import setting from '../../../src/color/setting';
import { States } from '../../../src/core/States';
// NOTE: Require ESM compatible libtess
-suite('color/Setting', function() {
+suite('color/Setting', function () {
let myp5; // sketch without WEBGL Mode
let my3D; // sketch with WEBGL mode
@@ -28,10 +28,10 @@ suite('color/Setting', function() {
delete mockP5Prototype._colorMaxes;
});
- beforeEach(async function() {
+ beforeEach(async function () {
await new Promise(resolve => {
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, p.WEBGL);
my3D = p;
resolve();
@@ -40,8 +40,8 @@ suite('color/Setting', function() {
});
await new Promise(resolve => {
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
resolve();
};
@@ -49,12 +49,12 @@ suite('color/Setting', function() {
});
});
- afterEach(function() {
+ afterEach(function () {
myp5.remove();
my3D.remove();
});
- suite('p5.prototype.erase', function() {
+ suite('p5.prototype.erase', function () {
beforeEach(() => {
mockP5Prototype._renderer = {
erase: vi.fn(),
@@ -64,7 +64,7 @@ suite('color/Setting', function() {
rgb: [255, 255, 255, 255],
hsb: [360, 100, 100, 1],
hsl: [360, 100, 100, 1],
- clone: function() {
+ clone: function () {
// Quick incorrect implementation for this test
return this;
}
@@ -77,38 +77,38 @@ suite('color/Setting', function() {
vi.resetAllMocks();
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.erase);
});
- test('should call the renderer erase method with default arguments', function() {
+ test('should call the renderer erase method with default arguments', function () {
mockP5Prototype.erase();
expect(mockP5Prototype._renderer.erase).toHaveBeenCalledWith(255, 255);
});
// TODO: test in renderer
test.todo('should set renderer to erasing state');
- test.todo('should cache renderer fill', function() {
+ test.todo('should cache renderer fill', function () {
myp5.fill(255, 0, 0);
const fillStyle = myp5.drawingContext.fillStyle;
myp5.erase();
assert.deepEqual(myp5._renderer._cachedFillStyle, fillStyle);
});
- test.todo('should cache renderer stroke', function() {
+ test.todo('should cache renderer stroke', function () {
myp5.stroke(255, 0, 0);
const strokeStyle = myp5.drawingContext.strokeStyle;
myp5.erase();
assert.deepEqual(myp5._renderer._cachedStrokeStyle, strokeStyle);
});
- test.todo('should cache renderer blend', function() {
+ test.todo('should cache renderer blend', function () {
myp5.blendMode(myp5.SCREEN);
myp5.erase();
assert.deepEqual(myp5._renderer._cachedBlendMode, myp5.SCREEN);
});
- test.todo('should set fill strength', function() {
+ test.todo('should set fill strength', function () {
myp5.erase(125);
assert.equal(
myp5.color(myp5.drawingContext.fillStyle).array,
@@ -116,7 +116,7 @@ suite('color/Setting', function() {
);
});
- test.todo('should set stroke strength', function() {
+ test.todo('should set stroke strength', function () {
myp5.erase(255, 50);
assert.equal(
myp5.color(myp5.drawingContext.strokeStyle).array,
@@ -125,67 +125,71 @@ suite('color/Setting', function() {
});
});
- suite('p5.RendererGL.prototype.erase', function() {
- test('should set renderer to erasing state', function() {
+ suite('p5.RendererGL.prototype.erase', function () {
+ test('should set renderer to erasing state', function () {
my3D.erase();
assert.isTrue(my3D._renderer._isErasing);
});
- test.todo('should cache renderer fill', function() {
+ test.todo('should cache renderer fill', function () {
my3D.fill(255, 0, 0);
const curFillColor = my3D._renderer.states.curFillColor;
my3D.erase();
assert.deepEqual(my3D._renderer._cachedFillStyle, curFillColor);
});
- test.todo('should cache renderer stroke', function() {
+ test.todo('should cache renderer stroke', function () {
my3D.stroke(255, 0, 0);
const strokeStyle = my3D._renderer.states.curStrokeColor;
my3D.erase();
assert.deepEqual(my3D._renderer._cachedStrokeStyle, strokeStyle);
});
- test('should cache renderer blend', function() {
+ test('should cache renderer blend', function () {
my3D.blendMode(my3D.SCREEN);
my3D.erase();
assert.deepEqual(my3D._renderer.preEraseBlend, my3D.SCREEN);
});
- test('should set fill strength', function() {
+ test('should set fill strength', function () {
my3D.erase(125);
- assert.deepEqual(
- my3D._renderer.states.curFillColor,
- [1, 1, 1, 125 / 255]
- );
+ assert.deepEqual(my3D._renderer.states.curFillColor, [
+ 1,
+ 1,
+ 1,
+ 125 / 255
+ ]);
});
- test('should set stroke strength', function() {
+ test('should set stroke strength', function () {
my3D.erase(255, 50);
- assert.deepEqual(
- my3D._renderer.states.curStrokeColor,
- [1, 1, 1, 50 / 255]
- );
+ assert.deepEqual(my3D._renderer.states.curStrokeColor, [
+ 1,
+ 1,
+ 1,
+ 50 / 255
+ ]);
});
- test('should set default values when no arguments', function() {
+ test('should set default values when no arguments', function () {
my3D.erase();
assert.deepEqual(my3D._renderer.states.curFillColor, [1, 1, 1, 1]);
assert.deepEqual(my3D._renderer.states.curStrokeColor, [1, 1, 1, 1]);
});
});
- suite('p5.prototype.noErase', function() {
- test('should be a function', function() {
+ suite('p5.prototype.noErase', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.noErase);
});
- test('should turn off renderer erasing state', function() {
+ test('should turn off renderer erasing state', function () {
myp5.erase();
myp5.noErase();
assert.isFalse(myp5._renderer._isErasing);
});
- test.todo('should restore cached renderer fill', function() {
+ test.todo('should restore cached renderer fill', function () {
myp5.fill(255, 0, 0);
const fillStyle = myp5.drawingContext.fillStyle;
myp5.erase();
@@ -193,7 +197,7 @@ suite('color/Setting', function() {
assert.deepEqual(myp5.drawingContext.fillStyle, fillStyle);
});
- test.todo('should restore cached renderer stroke', function() {
+ test.todo('should restore cached renderer stroke', function () {
myp5.stroke(255, 0, 0);
const strokeStyle = myp5.drawingContext.strokeStyle;
myp5.erase();
@@ -202,14 +206,14 @@ suite('color/Setting', function() {
});
});
- suite('p5.RendererGL.prototype.noErase', function() {
- test('should turn off renderer erasing state', function() {
+ suite('p5.RendererGL.prototype.noErase', function () {
+ test('should turn off renderer erasing state', function () {
my3D.erase();
my3D.noErase();
assert.isFalse(my3D._renderer._isErasing);
});
- test.todo('should restore cached renderer fill', function() {
+ test.todo('should restore cached renderer fill', function () {
my3D.fill(255, 0, 0);
const fillStyle = my3D._renderer.states.curFillColor.slice();
my3D.erase();
@@ -217,7 +221,7 @@ suite('color/Setting', function() {
assert.deepEqual([1, 0, 0, 1], fillStyle);
});
- test.todo('should restore cached renderer stroke', function() {
+ test.todo('should restore cached renderer stroke', function () {
my3D.stroke(255, 0, 0);
const strokeStyle = my3D._renderer.states.curStrokeColor.slice();
my3D.erase();
@@ -226,60 +230,96 @@ suite('color/Setting', function() {
});
});
- suite('p5.prototype.colorMode', function() {
- test('should be a function', function() {
+ suite('p5.prototype.colorMode', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.colorMode);
});
- test('should set mode to RGB', function() {
+ test('should set mode to RGB', function () {
mockP5Prototype.colorMode('rgb');
assert.equal(mockP5Prototype._renderer.states.colorMode, 'rgb');
});
- test('should correctly set color RGB maxes', function() {
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['rgb'], [255, 255, 255, 255]);
+ test('should correctly set color RGB maxes', function () {
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['rgb'],
+ [255, 255, 255, 255]
+ );
mockP5Prototype.colorMode('rgb', 1, 1, 1);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['rgb'], [1, 1, 1, 255]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['rgb'],
+ [1, 1, 1, 255]
+ );
mockP5Prototype.colorMode('rgb', 1);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['rgb'], [1, 1, 1, 1]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['rgb'],
+ [1, 1, 1, 1]
+ );
mockP5Prototype.colorMode('rgb', 255, 255, 255, 1);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['rgb'], [255, 255, 255, 1]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['rgb'],
+ [255, 255, 255, 1]
+ );
mockP5Prototype.colorMode('rgb', 255);
});
- test('should set mode to HSL', function() {
+ test('should set mode to HSL', function () {
mockP5Prototype.colorMode('hsl');
assert.equal(mockP5Prototype._renderer.states.colorMode, 'hsl');
});
- test('should correctly set color HSL maxes', function() {
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['hsl'], [360, 100, 100, 1]);
+ test('should correctly set color HSL maxes', function () {
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['hsl'],
+ [360, 100, 100, 1]
+ );
mockP5Prototype.colorMode('hsl', 255, 255, 255);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['hsl'], [255, 255, 255, 1]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['hsl'],
+ [255, 255, 255, 1]
+ );
mockP5Prototype.colorMode('hsl', 360);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['hsl'], [360, 360, 360, 360]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['hsl'],
+ [360, 360, 360, 360]
+ );
mockP5Prototype.colorMode('hsl', 360, 100, 100, 1);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['hsl'], [360, 100, 100, 1]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['hsl'],
+ [360, 100, 100, 1]
+ );
});
- test('should set mode to HSB', function() {
+ test('should set mode to HSB', function () {
mockP5Prototype.colorMode('hsb');
assert.equal(mockP5Prototype._renderer.states.colorMode, 'hsb');
});
- test('should correctly set color HSB maxes', function() {
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['hsb'], [360, 100, 100, 1]);
+ test('should correctly set color HSB maxes', function () {
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['hsb'],
+ [360, 100, 100, 1]
+ );
mockP5Prototype.colorMode('hsb', 255, 255, 255);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['hsb'], [255, 255, 255, 1]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['hsb'],
+ [255, 255, 255, 1]
+ );
mockP5Prototype.colorMode('hsb', 360);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['hsb'], [360, 360, 360, 360]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['hsb'],
+ [360, 360, 360, 360]
+ );
mockP5Prototype.colorMode('hsb', 360, 100, 100, 1);
- assert.deepEqual(mockP5Prototype._renderer.states.colorMaxes['hsb'], [360, 100, 100, 1]);
+ assert.deepEqual(
+ mockP5Prototype._renderer.states.colorMaxes['hsb'],
+ [360, 100, 100, 1]
+ );
});
});
- suite('p5.Color components', function() {
- test('setRed() correctly sets red component', function() {
+ suite('p5.Color components', function () {
+ test('setRed() correctly sets red component', function () {
mockP5Prototype.colorMode('rgb', 255);
const c = mockP5Prototype.color(0, 162, 205, 255);
c.setRed(100);
@@ -289,7 +329,7 @@ suite('color/Setting', function() {
assert.equal(mockP5Prototype.alpha(c), 255);
});
- test('setGreen() correctly sets green component', function() {
+ test('setGreen() correctly sets green component', function () {
mockP5Prototype.colorMode('rgb', 255);
const c = mockP5Prototype.color(0, 162, 205, 255);
c.setGreen(100);
@@ -299,7 +339,7 @@ suite('color/Setting', function() {
assert.equal(mockP5Prototype.alpha(c), 255);
});
- test('setBlue() correctly sets blue component', function() {
+ test('setBlue() correctly sets blue component', function () {
mockP5Prototype.colorMode('rgb', 255);
const c = mockP5Prototype.color(0, 162, 205, 255);
c.setBlue(100);
@@ -309,7 +349,7 @@ suite('color/Setting', function() {
assert.equal(mockP5Prototype.alpha(c), 255);
});
- test('setAlpha correctly sets alpha component', function() {
+ test('setAlpha correctly sets alpha component', function () {
mockP5Prototype.colorMode('rgb', 255);
const c = mockP5Prototype.color(0, 162, 205, 255);
c.setAlpha(100);
@@ -319,32 +359,32 @@ suite('color/Setting', function() {
assert.equal(mockP5Prototype.alpha(c), 100);
});
- test('changing the red/green/blue/alpha components should clear the cached HSL/HSB values', function() {
+ test('changing the red/green/blue/alpha components should clear the cached HSL/HSB values', function () {
mockP5Prototype.colorMode('rgb', 255);
const c = mockP5Prototype.color(0, 162, 205, 255);
// create HSL/HSB values
mockP5Prototype.lightness(c);
mockP5Prototype.brightness(c);
- c.setRed(100/255);
+ c.setRed(100 / 255);
assert(!c.hsba);
assert(!c.hsla);
mockP5Prototype.lightness(c);
mockP5Prototype.brightness(c);
- c.setGreen(100/255);
+ c.setGreen(100 / 255);
assert(!c.hsba);
assert(!c.hsla);
mockP5Prototype.lightness(c);
mockP5Prototype.brightness(c);
- c.setBlue(100/255);
+ c.setBlue(100 / 255);
assert(!c.hsba);
assert(!c.hsla);
mockP5Prototype.lightness(c);
mockP5Prototype.brightness(c);
- c.setAlpha(100/255);
+ c.setAlpha(100 / 255);
assert(!c.hsba);
assert(!c.hsla);
});
diff --git a/test/unit/core/2d_primitives.js b/test/unit/core/2d_primitives.js
index a85e07ae87..528463fde8 100644
--- a/test/unit/core/2d_primitives.js
+++ b/test/unit/core/2d_primitives.js
@@ -1,68 +1,68 @@
import { mockP5, mockP5Prototype } from '../../js/mocks';
import primitives from '../../../src/shape/2d_primitives';
-suite('2D Primitives', function() {
- beforeAll(function() {
+suite('2D Primitives', function () {
+ beforeAll(function () {
primitives(mockP5, mockP5Prototype);
});
- suite('p5.prototype.arc', function() {
- test('should be a function', function() {
+ suite('p5.prototype.arc', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.arc);
assert.typeOf(mockP5Prototype.arc, 'function');
});
});
- suite('p5.prototype.ellipse', function() {
- test('should be a function', function() {
+ suite('p5.prototype.ellipse', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.ellipse);
assert.typeOf(mockP5Prototype.ellipse, 'function');
});
});
- suite('p5.prototype.line', function() {
- test('should be a function', function() {
+ suite('p5.prototype.line', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.line);
assert.typeOf(mockP5Prototype.line, 'function');
});
});
- suite('p5.prototype.point', function() {
- test('should be a function', function() {
+ suite('p5.prototype.point', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.point);
assert.typeOf(mockP5Prototype.point, 'function');
});
});
- suite('p5.prototype.quad', function() {
- test('should be a function', function() {
+ suite('p5.prototype.quad', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.quad);
assert.typeOf(mockP5Prototype.quad, 'function');
});
});
- suite('p5.prototype.rect', function() {
- test('should be a function', function() {
+ suite('p5.prototype.rect', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.rect);
assert.typeOf(mockP5Prototype.rect, 'function');
});
});
- suite('p5.prototype.triangle', function() {
- test('should be a function', function() {
+ suite('p5.prototype.triangle', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.triangle);
assert.typeOf(mockP5Prototype.triangle, 'function');
});
});
- suite('p5.prototype.square', function() {
- test('should be a function', function() {
+ suite('p5.prototype.square', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.square);
assert.typeOf(mockP5Prototype.square, 'function');
});
});
- suite('p5.prototype._normalizeArcAngles', function() {
- test('start/stop both at zero', function() {
+ suite('p5.prototype._normalizeArcAngles', function () {
+ test('start/stop both at zero', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -79,7 +79,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop same but non-zero', function() {
+ test('start/stop same but non-zero', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -96,7 +96,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop both close to zero, start < stop', function() {
+ test('start/stop both close to zero, start < stop', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -113,7 +113,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop both close to zero, start > stop', function() {
+ test('start/stop both close to zero, start > stop', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -130,7 +130,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop both close to same non-zero, start < stop', function() {
+ test('start/stop both close to same non-zero, start < stop', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -147,7 +147,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop both close to same non-zero, start > stop', function() {
+ test('start/stop both close to same non-zero, start > stop', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -164,7 +164,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop around zero but not close, start < stop', function() {
+ test('start/stop around zero but not close, start < stop', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -181,7 +181,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop around zero but not close, start > stop', function() {
+ test('start/stop around zero but not close, start > stop', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -198,7 +198,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop away from zero and not close, start < stop', function() {
+ test('start/stop away from zero and not close, start < stop', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -215,7 +215,7 @@ suite('2D Primitives', function() {
}
}
});
- test('start/stop away from zero and not close, start > stop', function() {
+ test('start/stop away from zero and not close, start > stop', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -232,7 +232,7 @@ suite('2D Primitives', function() {
}
}
});
- test('scaling correction, quadrants 1 and 3', function() {
+ test('scaling correction, quadrants 1 and 3', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
@@ -249,7 +249,7 @@ suite('2D Primitives', function() {
}
}
});
- test('scaling correction, quadrants 2 and 4', function() {
+ test('scaling correction, quadrants 2 and 4', function () {
var i, j, angles;
for (i = -2; i <= 2; i++) {
for (j = -2; j <= 2; j++) {
diff --git a/test/unit/core/States.js b/test/unit/core/States.js
index d4b2b56a35..56288a490a 100644
--- a/test/unit/core/States.js
+++ b/test/unit/core/States.js
@@ -67,4 +67,4 @@ suite('States', function () {
assert.equal(s.fill, 'red');
assert.deepEqual(s.getModified(), {});
});
-});
\ No newline at end of file
+});
diff --git a/test/unit/core/attributes.js b/test/unit/core/attributes.js
index 6cd0f416e2..c5c7997c38 100644
--- a/test/unit/core/attributes.js
+++ b/test/unit/core/attributes.js
@@ -1,55 +1,55 @@
import { mockP5, mockP5Prototype } from '../../js/mocks';
import attributes from '../../../src/shape/attributes';
-suite('Attributes', function() {
- beforeAll(function() {
+suite('Attributes', function () {
+ beforeAll(function () {
attributes(mockP5, mockP5Prototype);
});
- suite('p5.prototype.ellipseMode', function() {
- test('should be a function', function() {
+ suite('p5.prototype.ellipseMode', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.ellipseMode);
assert.typeOf(mockP5Prototype.ellipseMode, 'function');
});
});
- suite('p5.prototype.rectMode', function() {
- test('should be a function', function() {
+ suite('p5.prototype.rectMode', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.rectMode);
assert.typeOf(mockP5Prototype.rectMode, 'function');
});
});
- suite('p5.prototype.noSmooth', function() {
- test('should be a function', function() {
+ suite('p5.prototype.noSmooth', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.noSmooth);
assert.typeOf(mockP5Prototype.noSmooth, 'function');
});
});
- suite('p5.prototype.smooth', function() {
- test('should be a function', function() {
+ suite('p5.prototype.smooth', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.smooth);
assert.typeOf(mockP5Prototype.smooth, 'function');
});
});
- suite('p5.prototype.strokeCap', function() {
- test('should be a function', function() {
+ suite('p5.prototype.strokeCap', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.strokeCap);
assert.typeOf(mockP5Prototype.strokeCap, 'function');
});
});
- suite('p5.prototype.strokeJoin', function() {
- test('should be a function', function() {
+ suite('p5.prototype.strokeJoin', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.strokeJoin);
assert.typeOf(mockP5Prototype.strokeJoin, 'function');
});
});
- suite('p5.prototype.strokeWeight', function() {
- test('should be a function', function() {
+ suite('p5.prototype.strokeWeight', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.strokeWeight);
assert.typeOf(mockP5Prototype.strokeWeight, 'function');
});
diff --git a/test/unit/core/curves.js b/test/unit/core/curves.js
index 0e79fd6863..2be8db6ecd 100644
--- a/test/unit/core/curves.js
+++ b/test/unit/core/curves.js
@@ -2,8 +2,8 @@ import { mockP5, mockP5Prototype } from '../../js/mocks';
import curves from '../../../src/shape/curves';
import { States } from '../../../src/core/States';
-suite('Curves', function() {
- beforeAll(function() {
+suite('Curves', function () {
+ beforeAll(function () {
mockP5Prototype._renderer = {
states: new States({
splineProperties: {
@@ -18,65 +18,65 @@ suite('Curves', function() {
delete mockP5Prototype._renderer;
});
- suite('p5.prototype.bezier', function() {
- test('should be a function', function() {
+ suite('p5.prototype.bezier', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.bezier);
assert.typeOf(mockP5Prototype.bezier, 'function');
});
});
- suite('p5.prototype.bezierPoint', function() {
+ suite('p5.prototype.bezierPoint', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.bezierPoint);
assert.typeOf(mockP5Prototype.bezierPoint, 'function');
});
- test('should return the correct point on a Bezier Curve', function() {
+ test('should return the correct point on a Bezier Curve', function () {
result = mockP5Prototype.bezierPoint(85, 10, 90, 15, 0.5);
assert.equal(result, 50);
assert.notEqual(result, -1);
});
});
- suite('p5.prototype.bezierTangent', function() {
+ suite('p5.prototype.bezierTangent', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.bezierTangent);
assert.typeOf(mockP5Prototype.bezierTangent, 'function');
});
- test('should return the correct point on a Bezier Curve', function() {
+ test('should return the correct point on a Bezier Curve', function () {
result = mockP5Prototype.bezierTangent(95, 73, 73, 15, 0.5);
assert.equal(result, -60);
});
});
- suite('p5.prototype.spline', function() {
- test('should be a function', function() {
+ suite('p5.prototype.spline', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.spline);
assert.typeOf(mockP5Prototype.spline, 'function');
});
});
- suite('p5.prototype.splinePoint', function() {
+ suite('p5.prototype.splinePoint', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.splinePoint);
assert.typeOf(mockP5Prototype.splinePoint, 'function');
});
- test('should return the correct point on a Catmull-Rom Curve', function() {
+ test('should return the correct point on a Catmull-Rom Curve', function () {
result = mockP5Prototype.splinePoint(5, 5, 73, 73, 0.5);
assert.equal(result, 39);
assert.notEqual(result, -1);
});
});
- suite('p5.prototype.splineTangent', function() {
+ suite('p5.prototype.splineTangent', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.splineTangent);
assert.typeOf(mockP5Prototype.splineTangent, 'function');
});
- test('should return the correct point on a Catmull-Rom Curve', function() {
+ test('should return the correct point on a Catmull-Rom Curve', function () {
result = mockP5Prototype.splineTangent(95, 73, 73, 15, 0.5);
assert.equal(result, 10);
assert.notEqual(result, -1);
diff --git a/test/unit/core/environment.js b/test/unit/core/environment.js
index 590865c7dd..9438e5c9e3 100644
--- a/test/unit/core/environment.js
+++ b/test/unit/core/environment.js
@@ -1,32 +1,32 @@
import p5 from '../../../src/app.js';
import { vi } from 'vitest';
-suite('Environment', function() {
+suite('Environment', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.frameCount', function() {
- test('starts at zero', function() {
- return new Promise(function(resolve, reject) {
+ suite('p5.frameCount', function () {
+ test('starts at zero', function () {
+ return new Promise(function (resolve, reject) {
// Has to use a custom p5 to hook setup correctly
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
if (p.frameCount !== 0) {
reject('frameCount is not 0 in setup');
}
};
- p.draw = function() {
+ p.draw = function () {
if (p.frameCount === 1) {
resolve();
}
@@ -34,11 +34,11 @@ suite('Environment', function() {
});
});
});
- test('matches draw calls', function() {
- return new Promise(function(resolve, reject) {
+ test('matches draw calls', function () {
+ return new Promise(function (resolve, reject) {
var frames = myp5.frameCount;
var start = myp5.frameCount;
- myp5.draw = function() {
+ myp5.draw = function () {
try {
frames += 1;
assert.equal(myp5.frameCount, frames);
@@ -69,41 +69,41 @@ suite('Environment', function() {
});
});
- suite('p5.prototype.focused', function() {
- test('it should return true on focus', function() {
+ suite('p5.prototype.focused', function () {
+ test('it should return true on focus', function () {
window.dispatchEvent(new Event('focus'));
assert.strictEqual(myp5.focused, true);
});
- test('it should return true on blur', function() {
+ test('it should return true on blur', function () {
window.dispatchEvent(new Event('blur'));
assert.strictEqual(myp5.focused, false);
});
});
- suite('p5.prototype.cursor', function() {
- test('should change cursor to cross', function() {
+ suite('p5.prototype.cursor', function () {
+ test('should change cursor to cross', function () {
myp5.cursor(myp5.CROSS);
assert.strictEqual(myp5._curElement.elt.style.cursor, 'crosshair');
});
});
- suite('p5.prototype.noCursor', function() {
- test('should change cursor to none', function() {
+ suite('p5.prototype.noCursor', function () {
+ test('should change cursor to none', function () {
myp5.noCursor();
assert.strictEqual(myp5._curElement.elt.style.cursor, 'none');
});
});
- suite('p5.prototype.frameRate', function() {
- test.todo('returns 0 on first draw call', function() {
+ suite('p5.prototype.frameRate', function () {
+ test.todo('returns 0 on first draw call', function () {
assert.strictEqual(myp5.frameRate(), 0);
});
- test('returns current frame rate after first draw call', function() {
- return new Promise(function(resolve, reject) {
- new p5(function(p) {
- p.draw = function() {
+ test('returns current frame rate after first draw call', function () {
+ return new Promise(function (resolve, reject) {
+ new p5(function (p) {
+ p.draw = function () {
if (p.frameCount === 2 && p.frameRate() > 0) {
resolve();
p.remove();
@@ -113,17 +113,17 @@ suite('Environment', function() {
});
});
- test.todo('p5.prototype.getFrameRate', function() {
+ test.todo('p5.prototype.getFrameRate', function () {
assert.strictEqual(myp5.getFrameRate(), 0);
});
- suite.todo('drawing with target frame rates', function() {
+ suite.todo('drawing with target frame rates', function () {
let clock;
let prevRequestAnimationFrame;
let nextFrameCallback = () => {};
let controlledP5;
- beforeEach(function() {
+ beforeEach(function () {
clock = vi.useFakeTimers(0);
vi.spyOn(window.performance, 'now', Date.now);
@@ -131,45 +131,51 @@ suite('Environment', function() {
prevRequestAnimationFrame = window.requestAnimationFrame;
// Use a fake requestAnimationFrame that just stores a ref to the callback
// so that we can call it manually
- window.requestAnimationFrame = function(cb) {
+ window.requestAnimationFrame = function (cb) {
nextFrameCallback = cb;
};
- return new Promise(function(resolve) {
- controlledP5 = new p5(function(p) {
- p.setup = function() {
+ return new Promise(function (resolve) {
+ controlledP5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(10, 10);
p.frameRate(60);
p.loop();
resolve(p);
};
- p.draw = function() {};
+ p.draw = function () {};
});
});
});
- afterEach(function() {
+ afterEach(function () {
// clock.restore();
vi.restoreAllMocks();
window.performance.now.restore();
window.requestAnimationFrame = prevRequestAnimationFrame;
- nextFrameCallback = function() {};
+ nextFrameCallback = function () {};
controlledP5.remove();
});
- test('draw() is called at the correct frame rate given a faster display', function() {
+ test('draw() is called at the correct frame rate given a faster display', function () {
vi.spyOn(controlledP5, 'draw');
clock.tick(1000 / 200); // Simulate a 200Hz refresh rate
nextFrameCallback(); // trigger the next requestAnimationFrame
- assert(controlledP5.draw.notCalled, 'draw() should not be called before 1s/60');
+ assert(
+ controlledP5.draw.notCalled,
+ 'draw() should not be called before 1s/60'
+ );
// Advance until 5ms before the next frame should render.
// This should be within p5's threshold for rendering the frame.
clock.tick(1000 / 60 - 1000 / 200 - 5);
nextFrameCallback(); // trigger the next requestAnimationFrame
- assert(controlledP5.draw.calledOnce, 'one frame should have been drawn');
+ assert(
+ controlledP5.draw.calledOnce,
+ 'one frame should have been drawn'
+ );
// deltaTime should reflect real elapsed time
assert.equal(controlledP5.deltaTime, 1000 / 60 - 5);
@@ -178,7 +184,10 @@ suite('Environment', function() {
nextFrameCallback(); // trigger the next requestAnimationFrame
// Even though this is 1s/60 - 5ms from the last draw, the last frame came
// in early, so we still shouldn't draw
- assert(controlledP5.draw.calledOnce, 'draw() should not be called before 1s/60 past the last target draw time');
+ assert(
+ controlledP5.draw.calledOnce,
+ 'draw() should not be called before 1s/60 past the last target draw time'
+ );
// Advance enough time forward to be 1s/60 from the last draw
clock.tick(5);
@@ -190,66 +199,66 @@ suite('Environment', function() {
});
});
- suite('p5.prototype.getTargetFrameRate', function() {
- test('returns 60 on the first call', function() {
+ suite('p5.prototype.getTargetFrameRate', function () {
+ test('returns 60 on the first call', function () {
assert.strictEqual(myp5.getTargetFrameRate(), 60);
});
- test('returns set value of randomize integer', function() {
- let randVal = Math.floor(Math.random()*120);
+ test('returns set value of randomize integer', function () {
+ let randVal = Math.floor(Math.random() * 120);
myp5.frameRate(randVal);
assert.strictEqual(myp5.getTargetFrameRate(), randVal);
});
});
- suite('Canvas dimensions', function() {
- test('p5.prototype.width', function() {
+ suite('Canvas dimensions', function () {
+ test('p5.prototype.width', function () {
myp5.createCanvas(20, 30);
assert.strictEqual(myp5.width, 20);
});
- test('p5.prototype.height', function() {
+ test('p5.prototype.height', function () {
myp5.createCanvas(20, 30);
assert.strictEqual(myp5.height, 30);
});
});
- suite('p5.prototype.pixelDensity', function() {
- test('returns the pixel density', function() {
+ suite('p5.prototype.pixelDensity', function () {
+ test('returns the pixel density', function () {
assert.isNumber(myp5.pixelDensity());
});
- test('sets the pixel density', function() {
+ test('sets the pixel density', function () {
myp5.pixelDensity(2);
assert.strictEqual(myp5.pixelDensity(), 2);
});
});
- suite('p5.prototype.displayDensity', function() {
- test('returns the pixel density of the display', function() {
+ suite('p5.prototype.displayDensity', function () {
+ test('returns the pixel density of the display', function () {
assert.isNumber(myp5.displayDensity());
});
- test('pixelDensity does not change display density', function() {
+ test('pixelDensity does not change display density', function () {
let pd = myp5.displayDensity();
myp5.pixelDensity(pd + 1);
assert.isNumber(myp5.displayDensity(), pd);
});
});
- suite('2D context test', function() {
- beforeEach(function() {
+ suite('2D context test', function () {
+ beforeEach(function () {
myp5.createCanvas(100, 100);
});
- test('worldToScreen for 2D context', function() {
+ test('worldToScreen for 2D context', function () {
let worldPos = myp5.createVector(50, 50);
let screenPos = myp5.worldToScreen(worldPos);
assert.closeTo(screenPos.x, 50, 0.1);
assert.closeTo(screenPos.y, 50, 0.1);
});
- test('worldToScreen accepts numbers with translation in 2D', function() {
+ test('worldToScreen accepts numbers with translation in 2D', function () {
myp5.push();
myp5.translate(50, 50);
let screenPos = myp5.worldToScreen(0, 0);
@@ -258,7 +267,7 @@ suite('Environment', function() {
assert.closeTo(screenPos.y, 50, 0.1);
});
- test('worldToScreen with rotation in 2D', function() {
+ test('worldToScreen with rotation in 2D', function () {
myp5.push();
myp5.translate(50, 50);
myp5.rotate(myp5.PI / 2);
@@ -269,14 +278,14 @@ suite('Environment', function() {
assert.closeTo(screenPos.y, 60, 0.1);
});
- test('screenToWorld for 2D context', function() {
+ test('screenToWorld for 2D context', function () {
let screenPos = myp5.createVector(50, 50);
let worldPos = myp5.screenToWorld(screenPos);
assert.closeTo(worldPos.x, 50, 0.1);
assert.closeTo(worldPos.y, 50, 0.1);
});
- test('screenToWorld accepts numbers with translation in 2D', function() {
+ test('screenToWorld accepts numbers with translation in 2D', function () {
myp5.push();
myp5.translate(50, 50);
let worldPos = myp5.screenToWorld(50, 50);
@@ -285,7 +294,7 @@ suite('Environment', function() {
assert.closeTo(worldPos.y, 0, 0.1);
});
- test('screenToWorld with rotation in 2D', function() {
+ test('screenToWorld with rotation in 2D', function () {
myp5.push();
myp5.translate(50, 50);
myp5.rotate(myp5.PI / 2);
@@ -296,7 +305,7 @@ suite('Environment', function() {
assert.closeTo(worldPos.y, 0, 0.1);
});
- test('screenToWorld is inverse of worldToScreen in 2D', function() {
+ test('screenToWorld is inverse of worldToScreen in 2D', function () {
myp5.push();
// Arbitrary rotation
myp5.translate(10, 10);
@@ -310,19 +319,19 @@ suite('Environment', function() {
});
});
- suite('3D context test', function() {
- beforeEach(function() {
+ suite('3D context test', function () {
+ beforeEach(function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
});
- test('worldToScreen for 3D context', function() {
+ test('worldToScreen for 3D context', function () {
let worldPos = myp5.createVector(0, 0, 0);
let screenPos = myp5.worldToScreen(worldPos);
assert.closeTo(screenPos.x, 50, 0.1);
assert.closeTo(screenPos.y, 50, 0.1);
});
- test('worldToScreen accepts numbers with translation in 3D', function() {
+ test('worldToScreen accepts numbers with translation in 3D', function () {
myp5.push();
myp5.translate(50, 50, 0);
let screenPos = myp5.worldToScreen(0, 0, 0);
@@ -331,7 +340,7 @@ suite('Environment', function() {
assert.closeTo(screenPos.y, 100, 0.1);
});
- test('worldToScreen with rotation in 3D around Y-axis', function() {
+ test('worldToScreen with rotation in 3D around Y-axis', function () {
myp5.push();
myp5.rotateY(myp5.PI / 2);
let worldPos = myp5.createVector(50, 0, 0);
@@ -341,7 +350,7 @@ suite('Environment', function() {
assert.closeTo(screenPos.y, 50, 0.1);
});
- test('worldToScreen with rotation in 3D around Z-axis', function() {
+ test('worldToScreen with rotation in 3D around Z-axis', function () {
myp5.push();
myp5.rotateZ(myp5.PI / 2);
let worldPos = myp5.createVector(10, 0, 0);
@@ -351,7 +360,7 @@ suite('Environment', function() {
assert.closeTo(screenPos.y, 60, 0.1);
});
- test('screenToWorld for 3D context', function() {
+ test('screenToWorld for 3D context', function () {
let screenPos = myp5.createVector(50, 50);
let worldPos = myp5.screenToWorld(screenPos);
assert.closeTo(worldPos.x, 0, 0.1);
@@ -359,7 +368,7 @@ suite('Environment', function() {
assert.closeTo(worldPos.z, 0, 0.1);
});
- test('screenToWorld accepts numbers with translation in 3D', function() {
+ test('screenToWorld accepts numbers with translation in 3D', function () {
myp5.push();
myp5.translate(50, 50);
let worldPos = myp5.screenToWorld(100, 100);
@@ -368,7 +377,7 @@ suite('Environment', function() {
assert.closeTo(worldPos.y, 0, 0.1);
});
- test('screenToWorld with rotation in 3D around Y-axis', function() {
+ test('screenToWorld with rotation in 3D around Y-axis', function () {
myp5.push();
myp5.rotateY(myp5.PI / 2);
let screenPos = myp5.createVector(50, 50);
@@ -377,7 +386,7 @@ suite('Environment', function() {
assert.closeTo(worldPos.y, 0, 0.1);
});
- test('screenToWorld with rotation in 3D around Z-axis', function() {
+ test('screenToWorld with rotation in 3D around Z-axis', function () {
myp5.push();
myp5.rotateZ(myp5.PI / 2);
let screenPos = myp5.createVector(50, 60);
@@ -387,7 +396,7 @@ suite('Environment', function() {
assert.closeTo(worldPos.y, 0, 0.1);
});
- test('screenToWorld is inverse of worldToScreen in 3D', function() {
+ test('screenToWorld is inverse of worldToScreen in 3D', function () {
myp5.push();
// Arbitrary rotation
myp5.translate(10, 10);
diff --git a/test/unit/core/loading.js b/test/unit/core/loading.js
index c7ee619200..53659175ee 100644
--- a/test/unit/core/loading.js
+++ b/test/unit/core/loading.js
@@ -1,21 +1,21 @@
import { vi, suite, test, assert } from 'vitest';
import loading from '../../../src/core/loading.js';
-suite('Loading indicator', function() {
+suite('Loading indicator', function () {
let container;
let canvas;
const lifecycles = {};
loading(null, null, lifecycles);
- beforeEach(function() {
+ beforeEach(function () {
container = document.createElement('div');
canvas = document.createElement('canvas');
container.appendChild(canvas);
document.body.appendChild(container);
});
- afterEach(function() {
+ afterEach(function () {
if (container) {
container.remove();
container = null;
@@ -23,7 +23,7 @@ suite('Loading indicator', function() {
}
});
- test('shows a loading indicator while async setup waits for load()', async function() {
+ test('shows a loading indicator while async setup waits for load()', async function () {
let loadTest;
const p = {
@@ -57,8 +57,7 @@ suite('Loading indicator', function() {
p.circle(p.width / 2, p.height / 2, 100);
p.circle(p.mouseX, p.mouseY, 20);
- }
- finally {
+ } finally {
lifecycles.postsetup.call(p);
}
})();
@@ -74,10 +73,13 @@ suite('Loading indicator', function() {
assert.deepEqual(p.createCanvas.mock.calls, [[400, 400]]);
assert.deepEqual(p.background.mock.calls, [['#EB5580']]);
assert.deepEqual(p.fill.mock.calls, [[255]]);
- assert.deepEqual(p.circle.mock.calls, [[200, 200, 100], [12, 34, 20]]);
+ assert.deepEqual(p.circle.mock.calls, [
+ [200, 200, 100],
+ [12, 34, 20]
+ ]);
});
- test('test the loading indicator in an instance', function() {
+ test('test the loading indicator in an instance', function () {
const p = {
_userNode: container
};
@@ -89,13 +91,12 @@ suite('Loading indicator', function() {
assert.isNull(container.querySelector('.loading-indicator'));
});
- test('test multiple indicators for multiple instances', async function() {
+ test('test multiple indicators for multiple instances', async function () {
const instance1 = document.createElement('div');
const instance2 = document.createElement('div');
document.body.appendChild(instance1);
document.body.appendChild(instance2);
-
let resolveLoad1;
let resolveLoad2;
@@ -114,41 +115,54 @@ suite('Loading indicator', function() {
const p1 = { _userNode: instance1 };
const p2 = { _userNode: instance2 };
- const setup1 = (async function() {
+ const setup1 = (async function () {
lifecycles.presetup.call(p1);
try {
await load1(2000);
- }
- finally {
+ } finally {
lifecycles.postsetup.call(p1);
}
})();
- const setup2 = (async function() {
+ const setup2 = (async function () {
lifecycles.presetup.call(p2);
try {
await load2(4000);
- }
- finally {
+ } finally {
lifecycles.postsetup.call(p2);
}
})();
- assert.exists(instance1.querySelector('.loading-indicator'), 'Container 1 should have a spinner');
- assert.exists(instance2.querySelector('.loading-indicator'), 'Container 2 should have a spinner');
+ assert.exists(
+ instance1.querySelector('.loading-indicator'),
+ 'Container 1 should have a spinner'
+ );
+ assert.exists(
+ instance2.querySelector('.loading-indicator'),
+ 'Container 2 should have a spinner'
+ );
resolveLoad1();
await setup1;
- assert.isNull(instance1.querySelector('.loading-indicator'), 'Container 1 spinner should be removed');
- assert.exists(instance2.querySelector('.loading-indicator'), 'Container 2 spinner MUST still exist');
+ assert.isNull(
+ instance1.querySelector('.loading-indicator'),
+ 'Container 1 spinner should be removed'
+ );
+ assert.exists(
+ instance2.querySelector('.loading-indicator'),
+ 'Container 2 spinner MUST still exist'
+ );
resolveLoad2();
await setup2;
- assert.isNull(instance2.querySelector('.loading-indicator'), 'Container 2 spinner should now be removed');
+ assert.isNull(
+ instance2.querySelector('.loading-indicator'),
+ 'Container 2 spinner should now be removed'
+ );
instance1.remove();
instance2.remove();
});
-});
\ No newline at end of file
+});
diff --git a/test/unit/core/main.js b/test/unit/core/main.js
index 84e9eb00da..6421b3f090 100644
--- a/test/unit/core/main.js
+++ b/test/unit/core/main.js
@@ -1,5 +1,9 @@
import p5 from '../../../src/app.js';
-import { createP5Iframe, P5_SCRIPT_TAG, P5_SCRIPT_URL } from '../../js/p5_helpers';
+import {
+ createP5Iframe,
+ P5_SCRIPT_TAG,
+ P5_SCRIPT_URL
+} from '../../js/p5_helpers';
import { vi } from 'vitest';
suite('Core', function () {
@@ -105,9 +109,7 @@ suite('Core', function () {
try {
globalObject.text = 'hi';
bind('text', noop);
- expect(
- _friendlyErrorStub
- ).toHaveBeenCalledTimes(1);
+ expect(_friendlyErrorStub).toHaveBeenCalledTimes(1);
} finally {
vi.restoreAllMocks();
}
diff --git a/test/unit/core/p5.Graphics.js b/test/unit/core/p5.Graphics.js
index 00961911de..b24e1fd80f 100644
--- a/test/unit/core/p5.Graphics.js
+++ b/test/unit/core/p5.Graphics.js
@@ -1,18 +1,18 @@
import p5 from '../../../src/app.js';
import { vi } from 'vitest';
-suite('Graphics', function() {
+suite('Graphics', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
@@ -44,16 +44,15 @@ suite('Graphics', function() {
assert.strictEqual(real, expected, 'invalid number of pixels');
}
- suite('p5.prototype.createGraphics', function() {
- test('it creates a graphics', function() {
+ suite('p5.prototype.createGraphics', function () {
+ test('it creates a graphics', function () {
var graph = myp5.createGraphics(10, 17);
assert.isObject(graph);
});
-
});
- suite('p5.Graphics', function() {
- test('it has necessary properties', function() {
+ suite('p5.Graphics', function () {
+ test('it has necessary properties', function () {
var graph = myp5.createGraphics(10, 17);
assert.property(graph, 'width');
assert.property(graph, 'height');
@@ -63,27 +62,27 @@ suite('Graphics', function() {
assert.property(graph, 'pixels');
});
- test('it has consistent sizes', function() {
+ test('it has consistent sizes', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
assertValidGraphSizes(graph, 10, 17, 1);
});
- test('its canvas has consistent sizes', function() {
+ test('its canvas has consistent sizes', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
assertValidCanvasSizes(graph.canvas, 10, 17, 1);
});
- test('it has a valid pixels array', function() {
+ test('it has a valid pixels array', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
assertValidPixels(graph, 10, 17, 1);
});
});
- suite('p5.Graphics.pixelDensity', function() {
- test('it can change density', function() {
+ suite('p5.Graphics.pixelDensity', function () {
+ test('it can change density', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
assert.strictEqual(graph.pixelDensity(), 1);
@@ -91,7 +90,7 @@ suite('Graphics', function() {
assert.strictEqual(graph.pixelDensity(), 3);
});
- test('it keeps valid sizes after change', function() {
+ test('it keeps valid sizes after change', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
assertValidGraphSizes(graph, 10, 17, 1);
@@ -99,7 +98,7 @@ suite('Graphics', function() {
assertValidGraphSizes(graph, 10, 17, 3);
});
- test('its canvas keeps valid sizes after change', function() {
+ test('its canvas keeps valid sizes after change', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
assertValidCanvasSizes(graph.canvas, 10, 17, 1);
@@ -107,7 +106,7 @@ suite('Graphics', function() {
assertValidCanvasSizes(graph.canvas, 10, 17, 3);
});
- test('it keeps a valid pixel array after change', function() {
+ test('it keeps a valid pixel array after change', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
assertValidPixels(graph, 10, 17, 1);
@@ -117,15 +116,15 @@ suite('Graphics', function() {
});
});
- suite('p5.Graphics.prototype.splineVertex', function() {
- test('should be a function in graphics', function() {
+ suite('p5.Graphics.prototype.splineVertex', function () {
+ test('should be a function in graphics', function () {
let g = myp5.createGraphics(10, 10);
assert.ok(g.splineVertex);
assert.typeOf(g.splineVertex, 'function');
});
});
- suite('p5.Graphics.resizeCanvas', function() {
+ suite('p5.Graphics.resizeCanvas', function () {
let glStub;
afterEach(() => {
@@ -135,50 +134,50 @@ suite('Graphics', function() {
}
});
- test('it can call resizeCanvas', function() {
+ test('it can call resizeCanvas', function () {
var graph = myp5.createGraphics(10, 17);
- var resize = function() {
+ var resize = function () {
graph.resizeCanvas(19, 16);
};
assert.doesNotThrow(resize);
});
- test('it resizes properly with pixelDensity 1', function() {
+ test('it resizes properly with pixelDensity 1', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
graph.resizeCanvas(19, 16);
assertValidGraphSizes(graph, 19, 16, 1);
});
- test('its canvas resizes properly with pixelDensity 1', function() {
+ test('its canvas resizes properly with pixelDensity 1', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
graph.resizeCanvas(19, 16);
assertValidCanvasSizes(graph.canvas, 19, 16, 1);
});
- test('it resizes properly the pixels array with density 1', function() {
+ test('it resizes properly the pixels array with density 1', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(1);
graph.resizeCanvas(19, 16);
assertValidPixels(graph, 19, 16, 1);
});
- test('it resizes properly with pixelDensity 2', function() {
+ test('it resizes properly with pixelDensity 2', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(2);
graph.resizeCanvas(19, 16);
assertValidGraphSizes(graph, 19, 16, 2);
});
- test('its canvas resizes properly with pixelDensity 2', function() {
+ test('its canvas resizes properly with pixelDensity 2', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(2);
graph.resizeCanvas(19, 16);
assertValidCanvasSizes(graph.canvas, 19, 16, 2);
});
- test('it resizes properly the pixels array with density 2', function() {
+ test('it resizes properly the pixels array with density 2', function () {
var graph = myp5.createGraphics(10, 17);
graph.pixelDensity(2);
graph.resizeCanvas(19, 16);
@@ -186,7 +185,7 @@ suite('Graphics', function() {
});
// NOTE: check this
- test('it resizes the graphics based on max texture size', function() {
+ test('it resizes the graphics based on max texture size', function () {
glStub = vi.spyOn(p5.RendererGL.prototype, '_getMaxTextureSize');
const fakeMaxTextureSize = 100;
glStub.mockReturnValue(fakeMaxTextureSize);
@@ -196,8 +195,8 @@ suite('Graphics', function() {
});
});
- suite('p5.Graphics.remove()', function() {
- test('it sets properties to undefined after removal', function() {
+ suite('p5.Graphics.remove()', function () {
+ test('it sets properties to undefined after removal', function () {
var graph = myp5.createGraphics(10, 17);
graph.remove();
assert.isUndefined(graph.canvas);
diff --git a/test/unit/core/param_errors.js b/test/unit/core/param_errors.js
index 55cd19cf58..d46c37a70b 100644
--- a/test/unit/core/param_errors.js
+++ b/test/unit/core/param_errors.js
@@ -7,29 +7,29 @@ suite('Validate Params', function () {
Color: function () {
return 'mock p5.Color';
},
- Image: function() {
+ Image: function () {
return 'mock p5.Image';
},
- Element: function() {
+ Element: function () {
return 'mock p5.Element';
},
- Texture: function() {
+ Texture: function () {
return 'mock p5.Texture';
},
- Framebuffer: function() {
+ Framebuffer: function () {
return 'mock p5.Framebuffer';
},
- FramebufferTexture: function() {
+ FramebufferTexture: function () {
return 'mock p5.FramebufferTexture';
},
- Renderer: function() {
+ Renderer: function () {
return 'mock p5.Renderer';
},
- Graphics: function() {
+ Graphics: function () {
return 'mock p5.Graphics';
},
_error: () => {},
- registerDecorator: () => {},
+ registerDecorator: () => {}
};
const mockP5Prototype = {};
@@ -37,37 +37,45 @@ suite('Validate Params', function () {
validateParams(mockP5, mockP5Prototype, {});
});
- afterAll(function () {
- });
+ afterAll(function () {});
- suite('validateParams: multiple types allowed for single parameter', function () {
- test('saturation(): valid inputs', () => {
- const validInputs = [
- { input: ['rgb(255, 128, 128)'] },
- { input: [[0, 50, 100]] },
- { input: [new mockP5.Color()] }
- ];
+ suite(
+ 'validateParams: multiple types allowed for single parameter',
+ function () {
+ test('saturation(): valid inputs', () => {
+ const validInputs = [
+ { input: ['rgb(255, 128, 128)'] },
+ { input: [[0, 50, 100]] },
+ { input: [new mockP5.Color()] }
+ ];
- validInputs.forEach(({ input }) => {
- const result = mockP5Prototype._validate('saturation', input);
- assert.isTrue(result.success);
+ validInputs.forEach(({ input }) => {
+ const result = mockP5Prototype._validate('saturation', input);
+ assert.isTrue(result.success);
+ });
});
- });
- test('saturation(): invalid inputs', () => {
- const invalidInputs = [
- { input: [true] },
- { input: [42] },
- { input: [{}] },
- { input: [null] }
- ];
+ test('saturation(): invalid inputs', () => {
+ const invalidInputs = [
+ { input: [true] },
+ { input: [42] },
+ { input: [{}] },
+ { input: [null] }
+ ];
- invalidInputs.forEach(({ input }) => {
- const result = mockP5Prototype._validate('p5.saturation', input);
- assert.isTrue(result.error.toString().startsWith('Expected Color or array or string at the first parameter'));
+ invalidInputs.forEach(({ input }) => {
+ const result = mockP5Prototype._validate('p5.saturation', input);
+ assert.isTrue(
+ result.error
+ .toString()
+ .startsWith(
+ 'Expected Color or array or string at the first parameter'
+ )
+ );
+ });
});
- });
- });
+ }
+ );
suite('validateParams: constant as parameter', function () {
const validInputs = [
@@ -91,7 +99,10 @@ suite('Validate Params', function () {
invalidInputs.forEach(({ name, input }) => {
test(`blendMode(): ${name}`, () => {
const result = mockP5Prototype._validate('p5.blendMode', [input]);
- const expectedError = 'Expected constant (please refer to documentation for allowed values) at the first parameter, but received ' + input + ' in p5.blendMode().';
+ const expectedError =
+ 'Expected constant (please refer to documentation for allowed values) at the first parameter, but received ' +
+ input +
+ ' in p5.blendMode().';
assert.equal(result.error.toString(), expectedError);
});
});
@@ -99,8 +110,14 @@ suite('Validate Params', function () {
suite('validateParams: numbers + optional constant for arc()', function () {
const validInputs = [
- { name: 'no friendly-err-msg', input: [200, 100, 100, 80, 0, Math.PI, constants.PIE, 30] },
- { name: 'missing optional param #6 & #7, no friendly-err-msg', input: [200, 100, 100, 80, 0, Math.PI] }
+ {
+ name: 'no friendly-err-msg',
+ input: [200, 100, 100, 80, 0, Math.PI, constants.PIE, 30]
+ },
+ {
+ name: 'missing optional param #6 & #7, no friendly-err-msg',
+ input: [200, 100, 100, 80, 0, Math.PI]
+ }
];
validInputs.forEach(({ name, input }) => {
test(`arc(): ${name}`, () => {
@@ -110,11 +127,31 @@ suite('Validate Params', function () {
});
const invalidInputs = [
- { name: 'missing required arc parameters #4, #5', input: [200, 100, 100, 80], msg: 'Expected at least 6 arguments, but received fewer in p5.arc(). For more information, see https://p5js.org/reference/p5/arc.' },
- { name: 'missing required param #0', input: [undefined, 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: 'Expected number at the first parameter, but received undefined in p5.arc().' },
- { name: 'missing required param #4', input: [200, 100, 100, 80, undefined, 0], msg: 'Expected number at the fifth parameter, but received undefined in p5.arc().' },
- { name: 'missing optional param #5', input: [200, 100, 100, 80, 0, undefined, Math.PI], msg: 'Expected number at the sixth parameter, but received undefined in p5.arc().' },
- { name: 'wrong param type at #0', input: ['a', 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: 'Expected number at the first parameter, but received string in p5.arc().' }
+ {
+ name: 'missing required arc parameters #4, #5',
+ input: [200, 100, 100, 80],
+ msg: 'Expected at least 6 arguments, but received fewer in p5.arc(). For more information, see https://p5js.org/reference/p5/arc.'
+ },
+ {
+ name: 'missing required param #0',
+ input: [undefined, 100, 100, 80, 0, Math.PI, constants.PIE, 30],
+ msg: 'Expected number at the first parameter, but received undefined in p5.arc().'
+ },
+ {
+ name: 'missing required param #4',
+ input: [200, 100, 100, 80, undefined, 0],
+ msg: 'Expected number at the fifth parameter, but received undefined in p5.arc().'
+ },
+ {
+ name: 'missing optional param #5',
+ input: [200, 100, 100, 80, 0, undefined, Math.PI],
+ msg: 'Expected number at the sixth parameter, but received undefined in p5.arc().'
+ },
+ {
+ name: 'wrong param type at #0',
+ input: ['a', 100, 100, 80, 0, Math.PI, constants.PIE, 30],
+ msg: 'Expected number at the first parameter, but received string in p5.arc().'
+ }
];
invalidInputs.forEach(({ name, input, msg }) => {
@@ -127,7 +164,11 @@ suite('Validate Params', function () {
suite('validateParams: promise where no promise is expected', function () {
test('image(): promise for first argument', function () {
- const result = mockP5Prototype._validate('p5.image', [Promise.resolve(), 0, 0]);
+ const result = mockP5Prototype._validate('p5.image', [
+ Promise.resolve(),
+ 0,
+ 0
+ ]);
assert.equal(
result.error.toString(),
'Did you mean to put `await` before a loading function? An unexpected Promise was found. Expected Image or Element or Texture or Framebuffer or FramebufferTexture or Renderer or Graphics at the first parameter in p5.image().'
@@ -137,20 +178,57 @@ suite('Validate Params', function () {
suite('validateParams: class, multi-types + optional numbers', function () {
test('ambientLight(): no firendly-err-msg', function () {
- const result = mockP5Prototype._validate('p5.ambientLight', [new mockP5.Color()]);
+ const result = mockP5Prototype._validate('p5.ambientLight', [
+ new mockP5.Color()
+ ]);
assert.isTrue(result.success);
});
});
suite('validateParams: a few edge cases', function () {
const invalidInputs = [
- { fn: 'color', name: 'wrong type for optional parameter', input: [0, 0, 0, 'A'], msg: 'Expected number at the fourth parameter, but received string in p5.color().' },
- { fn: 'color', name: 'superfluous parameter', input: [[0, 0, 0], 0], msg: 'Expected number at the first parameter, but received array in p5.color().' },
- { fn: 'color', name: 'wrong element types', input: [['A', 'B', 'C']], msg: 'Expected number at the first parameter, but received array in p5.color().' },
- { fn: 'rect', name: 'null, non-trailing, optional parameter', input: [0, 0, 0, 0, null, 0, 0, 0], msg: 'Expected number at the fifth parameter, but received null in p5.rect().' },
- { fn: 'color', name: 'too many args + wrong types too', input: ['A', 'A', 0, 0, 0, 0, 0, 0, 0, 0], msg: 'Expected at most 4 arguments, but received more in p5.color(). For more information, see https://p5js.org/reference/p5/color.' },
- { fn: 'line', name: 'null string given', input: [1, 2, 4, 'null'], msg: 'Expected number at the fourth parameter, but received string in p5.line().' },
- { fn: 'line', name: 'NaN value given', input: [1, 2, 4, NaN], msg: 'Expected number at the fourth parameter, but received NaN in p5.line().' }
+ {
+ fn: 'color',
+ name: 'wrong type for optional parameter',
+ input: [0, 0, 0, 'A'],
+ msg: 'Expected number at the fourth parameter, but received string in p5.color().'
+ },
+ {
+ fn: 'color',
+ name: 'superfluous parameter',
+ input: [[0, 0, 0], 0],
+ msg: 'Expected number at the first parameter, but received array in p5.color().'
+ },
+ {
+ fn: 'color',
+ name: 'wrong element types',
+ input: [['A', 'B', 'C']],
+ msg: 'Expected number at the first parameter, but received array in p5.color().'
+ },
+ {
+ fn: 'rect',
+ name: 'null, non-trailing, optional parameter',
+ input: [0, 0, 0, 0, null, 0, 0, 0],
+ msg: 'Expected number at the fifth parameter, but received null in p5.rect().'
+ },
+ {
+ fn: 'color',
+ name: 'too many args + wrong types too',
+ input: ['A', 'A', 0, 0, 0, 0, 0, 0, 0, 0],
+ msg: 'Expected at most 4 arguments, but received more in p5.color(). For more information, see https://p5js.org/reference/p5/color.'
+ },
+ {
+ fn: 'line',
+ name: 'null string given',
+ input: [1, 2, 4, 'null'],
+ msg: 'Expected number at the fourth parameter, but received string in p5.line().'
+ },
+ {
+ fn: 'line',
+ name: 'NaN value given',
+ input: [1, 2, 4, NaN],
+ msg: 'Expected number at the fourth parameter, but received NaN in p5.line().'
+ }
];
invalidInputs.forEach(({ name, input, fn, msg }) => {
@@ -163,12 +241,27 @@ suite('Validate Params', function () {
suite('validateParams: trailing undefined arguments', function () {
const invalidInputs = [
- { fn: 'color', name: 'missing params #1, #2', input: [12, undefined, undefined], msg: 'Expected number at the second parameter, but received undefined in p5.color().' },
+ {
+ fn: 'color',
+ name: 'missing params #1, #2',
+ input: [12, undefined, undefined],
+ msg: 'Expected number at the second parameter, but received undefined in p5.color().'
+ },
// Even though the undefined arguments are technically allowed for
// optional parameters, it is more likely that the user wanted to call
// the function with meaningful arguments.
- { fn: 'random', name: 'missing params #0, #1', input: [undefined, undefined], msg: 'All arguments for p5.random() are undefined. There is likely an error in the code.' },
- { fn: 'circle', name: 'missing compulsory parameter #2', input: [5, 5, undefined], msg: 'Expected number at the third parameter, but received undefined in p5.circle().' }
+ {
+ fn: 'random',
+ name: 'missing params #0, #1',
+ input: [undefined, undefined],
+ msg: 'All arguments for p5.random() are undefined. There is likely an error in the code.'
+ },
+ {
+ fn: 'circle',
+ name: 'missing compulsory parameter #2',
+ input: [5, 5, undefined],
+ msg: 'Expected number at the third parameter, but received undefined in p5.circle().'
+ }
];
invalidInputs.forEach(({ fn, name, input, msg }) => {
@@ -193,10 +286,26 @@ suite('Validate Params', function () {
});
const invalidInputs = [
- { name: 'optional parameter, incorrect type', input: [65, 100, 100, 'a'], msg: 'Expected number at the fourth parameter, but received string in p5.color().' },
- { name: 'extra parameter', input: [[65, 100, 100], 100], msg: 'Expected number at the first parameter, but received array in p5.color().' },
- { name: 'incorrect element type', input: ['A', 'B', 'C'], msg: 'Expected number at the first parameter, but received string in p5.color().' },
- { name: 'incorrect parameter count', input: ['A', 'A', 0, 0, 0, 0, 0, 0], msg: 'Expected at most 4 arguments, but received more in p5.color(). For more information, see https://p5js.org/reference/p5/color.' }
+ {
+ name: 'optional parameter, incorrect type',
+ input: [65, 100, 100, 'a'],
+ msg: 'Expected number at the fourth parameter, but received string in p5.color().'
+ },
+ {
+ name: 'extra parameter',
+ input: [[65, 100, 100], 100],
+ msg: 'Expected number at the first parameter, but received array in p5.color().'
+ },
+ {
+ name: 'incorrect element type',
+ input: ['A', 'B', 'C'],
+ msg: 'Expected number at the first parameter, but received string in p5.color().'
+ },
+ {
+ name: 'incorrect parameter count',
+ input: ['A', 'A', 0, 0, 0, 0, 0, 0],
+ msg: 'Expected at most 4 arguments, but received more in p5.color(). For more information, see https://p5js.org/reference/p5/color.'
+ }
];
invalidInputs.forEach(({ name, input, msg }) => {
@@ -223,17 +332,29 @@ suite('Validate Params', function () {
test('set() with Boolean (invalid)', function () {
const result = mockP5Prototype._validate('p5.set', [0, 0, true]);
- assert.equal(result.error.toString(), 'Expected number or array or object at the third parameter, but received boolean in p5.set().');
+ assert.equal(
+ result.error.toString(),
+ 'Expected number or array or object at the third parameter, but received boolean in p5.set().'
+ );
});
});
- suite('validateParams: web API objects', function () { // TODO: fix this p5 error
+ suite('validateParams: web API objects', function () {
+ // TODO: fix this p5 error
const audioContext = new AudioContext();
const gainNode = audioContext.createGain();
const testCases = [
- { fn: 'mouseMoved', name: 'no friendly-err-msg', input: [new MouseEvent('click')] },
- { fn: 'p5.MediaElement.connect', name: 'no friendly-err-msg', input: [gainNode] }
+ {
+ fn: 'mouseMoved',
+ name: 'no friendly-err-msg',
+ input: [new MouseEvent('click')]
+ },
+ {
+ fn: 'p5.MediaElement.connect',
+ name: 'no friendly-err-msg',
+ input: [gainNode]
+ }
];
testCases.forEach(({ fn, name, input }) => {
@@ -251,30 +372,38 @@ suite('Validate Params', function () {
[new mockP5.Color(), 0.8],
[new mockP5.Color(), 0.5]
];
- const result = mockP5Prototype._validate('p5.paletteLerp', [colorStops, 0.5]);
+ const result = mockP5Prototype._validate('p5.paletteLerp', [
+ colorStops,
+ 0.5
+ ]);
assert.isTrue(result.success);
});
});
suite('validateParams: rest arguments', function () {
- test('createVector(): works with no args', function() {
+ test('createVector(): works with no args', function () {
const result = mockP5Prototype._validate('p5.createVector', []);
assert.isTrue(result.success);
});
- test('createVector(): works with one number', function() {
+ test('createVector(): works with one number', function () {
const result = mockP5Prototype._validate('p5.createVector', [1]);
assert.isTrue(result.success);
});
- test('createVector(): works with many numbers', function() {
+ test('createVector(): works with many numbers', function () {
const result = mockP5Prototype._validate('p5.createVector', [1, 2, 3, 4]);
assert.isTrue(result.success);
});
- test('createVector(): fails with a non-number', function() {
+ test('createVector(): fails with a non-number', function () {
const result = mockP5Prototype._validate('p5.createVector', ['1']);
assert.isFalse(result.success);
});
- test('createVector(): fails with any non-number', function() {
- const result = mockP5Prototype._validate('p5.createVector', [1, 2, '3', 4]);
+ test('createVector(): fails with any non-number', function () {
+ const result = mockP5Prototype._validate('p5.createVector', [
+ 1,
+ 2,
+ '3',
+ 4
+ ]);
assert.isFalse(result.success);
});
});
diff --git a/test/unit/core/properties.js b/test/unit/core/properties.js
index e7def6900e..2150ba0a42 100644
--- a/test/unit/core/properties.js
+++ b/test/unit/core/properties.js
@@ -1,10 +1,9 @@
import p5 from '../../../src/app.js';
-suite('Set/get properties', function() {
-
+suite('Set/get properties', function () {
let p = new p5(function (sketch) {
- sketch.setup = function () { };
- sketch.draw = function () { };
+ sketch.setup = function () {};
+ sketch.draw = function () {};
});
let getters = {
@@ -49,20 +48,21 @@ suite('Set/get properties', function() {
Object.keys(getters).forEach(prop => {
let arg = getters[prop];
- test(`${prop}()`, function() {
-
+ test(`${prop}()`, function () {
// setter
if (typeof arg === 'object' && !(arg instanceof p5.Color)) {
p[prop](...Object.values(arg)); // set with object
- }
- else if (Array.isArray(arg)) {
+ } else if (Array.isArray(arg)) {
p[prop](...arg); // set with array
- }
- else {
+ } else {
p[prop](arg); // set with primitive or p5.Color
}
// getter
- assert.strictEqual(p[prop]().toString(), arg.toString(), `${arg.toString()}`);
+ assert.strictEqual(
+ p[prop]().toString(),
+ arg.toString(),
+ `${arg.toString()}`
+ );
});
});
});
diff --git a/test/unit/core/rendering.js b/test/unit/core/rendering.js
index a7527d64b8..ee9b8d9d9e 100644
--- a/test/unit/core/rendering.js
+++ b/test/unit/core/rendering.js
@@ -1,30 +1,30 @@
import p5 from '../../../src/app.js';
import { vi, expect } from 'vitest';
-suite('Rendering', function() {
+suite('Rendering', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.prototype.webglVersion', function() {
- test('should return P2D if not using WebGL at all', function() {
+ suite('p5.prototype.webglVersion', function () {
+ test('should return P2D if not using WebGL at all', function () {
myp5.createCanvas(10, 10);
assert.equal(myp5.webglVersion, myp5.P2D);
});
});
- suite('p5.prototype.createCanvas', function() {
- test('should have correct initial colors', function() {
+ suite('p5.prototype.createCanvas', function () {
+ test('should have correct initial colors', function () {
var white = myp5.color(255, 255, 255)._array;
var black = myp5.color(0, 0, 0)._array;
assert.deepEqual(myp5.color(myp5._renderer._getFill())._array, white);
@@ -45,14 +45,14 @@ suite('Rendering', function() {
});
});
- suite('2D primitive Shape lifetime', function() {
- test('passes a fresh Shape to drawShape for each primitive', function() {
+ suite('2D primitive Shape lifetime', function () {
+ test('passes a fresh Shape to drawShape for each primitive', function () {
myp5.createCanvas(50, 50);
const renderer = myp5._renderer;
const originalDrawShape = renderer.drawShape;
const retainedShapes = [];
- renderer.drawShape = function(shape) {
+ renderer.drawShape = function (shape) {
retainedShapes.push(shape);
return originalDrawShape.call(this, shape);
};
@@ -77,7 +77,7 @@ suite('Rendering', function() {
});
});
- suite('p5.prototype.resizeCanvas', function() {
+ suite('p5.prototype.resizeCanvas', function () {
let glStub;
afterEach(() => {
@@ -87,37 +87,37 @@ suite('Rendering', function() {
}
});
- test('should resize canvas', function() {
+ test('should resize canvas', function () {
myp5.resizeCanvas(10, 20);
assert.equal(myp5.canvas.width, 10 * myp5.pixelDensity());
assert.equal(myp5.canvas.height, 20 * myp5.pixelDensity());
});
- test('should restore fill color', function() {
+ test('should restore fill color', function () {
myp5.fill('#f0f0f0');
myp5.resizeCanvas(10, 10);
assert.equal(myp5.drawingContext.fillStyle, '#f0f0f0');
});
- test('should restore stroke color', function() {
+ test('should restore stroke color', function () {
myp5.stroke('#f0f0f0');
myp5.resizeCanvas(10, 10);
assert.equal(myp5.drawingContext.strokeStyle, '#f0f0f0');
});
- test('should restore stroke weight', function() {
+ test('should restore stroke weight', function () {
myp5.strokeWeight(323);
myp5.resizeCanvas(10, 10);
assert.equal(myp5.drawingContext.lineWidth, 323);
});
- test('should restore stroke cap', function() {
+ test('should restore stroke cap', function () {
myp5.strokeCap(myp5.PROJECT);
myp5.resizeCanvas(10, 10);
assert.equal(myp5.drawingContext.lineCap, myp5.PROJECT);
});
- test.todo('should resize framebuffers', function() {
+ test.todo('should resize framebuffers', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
const fbo = myp5.createFramebuffer();
myp5.resizeCanvas(5, 15);
@@ -125,7 +125,7 @@ suite('Rendering', function() {
assert.equal(fbo.height, 15);
});
- test.todo('should resize graphic framebuffers', function() {
+ test.todo('should resize graphic framebuffers', function () {
const graphic = myp5.createGraphics(10, 10, myp5.WEBGL);
const fbo = graphic.createFramebuffer();
graphic.resizeCanvas(5, 15);
@@ -133,7 +133,7 @@ suite('Rendering', function() {
assert.equal(fbo.height, 15);
});
- test('should limit dimensions of canvas based on max texture size on create', function() {
+ test('should limit dimensions of canvas based on max texture size on create', function () {
glStub = vi.spyOn(p5.RendererGL.prototype, '_getMaxTextureSize');
const fakeMaxTextureSize = 100;
glStub.mockReturnValue(fakeMaxTextureSize);
@@ -144,7 +144,7 @@ suite('Rendering', function() {
assert.equal(myp5.height, 100);
});
- test('should limit dimensions of canvas based on max texture size on resize', function() {
+ test('should limit dimensions of canvas based on max texture size on resize', function () {
glStub = vi.spyOn(p5.RendererGL.prototype, '_getMaxTextureSize');
const fakeMaxTextureSize = 100;
glStub.mockReturnValue(fakeMaxTextureSize);
@@ -157,65 +157,88 @@ suite('Rendering', function() {
});
});
- suite('p5.prototype.blendMode', function() {
- var drawX = function() {
+ suite('p5.prototype.blendMode', function () {
+ var drawX = function () {
myp5.strokeWeight(30);
myp5.stroke(80, 150, 255);
myp5.line(25, 25, 75, 75);
myp5.stroke(255, 50, 50);
myp5.line(75, 25, 25, 75);
};
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(myp5.blendMode);
assert.typeOf(myp5.blendMode, 'function');
});
- test.todo('should be able to ADD', function() {
+ test.todo('should be able to ADD', function () {
myp5.blendMode(myp5.ADD);
drawX();
});
- test.todo('should be able to MULTIPLY', function() {
+ test.todo('should be able to MULTIPLY', function () {
myp5.blendMode(myp5.MULTIPLY);
drawX();
});
});
- suite('p5.prototype.setAttributes', function() {
- test.todo('_glAttributes should be null at start', function() {
+ suite('p5.prototype.setAttributes', function () {
+ test.todo('_glAttributes should be null at start', function () {
assert.deepEqual(myp5._glAttributes, null);
});
- test('_glAttributes should modify with setAttributes', function() {
+ test('_glAttributes should modify with setAttributes', function () {
myp5.setAttributes({ antialias: false, perPixelLighting: true });
assert.deepEqual(myp5._glAttributes.antialias, false);
assert.deepEqual(myp5._glAttributes.perPixelLighting, true);
});
- test('_glAttributes.antialias modify with smooth()', function() {
+ test('_glAttributes.antialias modify with smooth()', function () {
myp5.smooth();
assert.deepEqual(myp5._glAttributes.antialias, true);
});
});
var webglMethods = [
- 'rotateX', 'rotateY', 'rotateZ',
- 'camera', 'perspective', 'ortho', 'frustum', 'orbitControl',
- 'ambientLight', 'directionalLight', 'pointLight', 'lights', 'specularColor', 'spotLight',
+ 'rotateX',
+ 'rotateY',
+ 'rotateZ',
+ 'camera',
+ 'perspective',
+ 'ortho',
+ 'frustum',
+ 'orbitControl',
+ 'ambientLight',
+ 'directionalLight',
+ 'pointLight',
+ 'lights',
+ 'specularColor',
+ 'spotLight',
'model',
'shader',
- 'normalMaterial', 'texture', 'ambientMaterial', 'emissiveMaterial', 'specularMaterial',
- 'shininess', 'lightFalloff', 'metalness',
- 'plane', 'box', 'sphere', 'cylinder', 'cone', 'ellipsoid', 'torus'
+ 'normalMaterial',
+ 'texture',
+ 'ambientMaterial',
+ 'emissiveMaterial',
+ 'specularMaterial',
+ 'shininess',
+ 'lightFalloff',
+ 'metalness',
+ 'plane',
+ 'box',
+ 'sphere',
+ 'cylinder',
+ 'cone',
+ 'ellipsoid',
+ 'torus'
];
- suite.todo('webgl assertions', function() {
+ suite.todo('webgl assertions', function () {
for (var i = 0; i < webglMethods.length; i++) {
var webglMethod = webglMethods[i];
test(
webglMethod + '() should throw a WEBGL assertion Error',
- (function(webglMethod) {
- return function() {
+ (function (webglMethod) {
+ return function () {
var validateParamters = myp5.validateParameters;
myp5.validateParameters = false;
try {
- expect(function() {
+ expect(function () {
myp5[webglMethod].call(myp5);
}).toThrow(Error, /is only supported in WEBGL mode/);
} finally {
@@ -227,37 +250,37 @@ suite('Rendering', function() {
}
});
- suite('spline functions', function() {
- test('setting via splineProperty()', function() {
+ suite('spline functions', function () {
+ test('setting via splineProperty()', function () {
myp5.splineProperty('tightness', 2);
expect(myp5.splineProperty('tightness')).toEqual(2);
expect(myp5.splineProperties()).toMatchObject({ tightness: 2 });
});
- test('setting via splineProperties()', function() {
+ test('setting via splineProperties()', function () {
myp5.splineProperties({ tightness: 2 });
expect(myp5.splineProperty('tightness')).toEqual(2);
expect(myp5.splineProperties()).toMatchObject({ tightness: 2 });
});
});
- suite('set() with p5.Graphics', function() {
+ suite('set() with p5.Graphics', function () {
let myp5;
- beforeEach(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {
+ beforeEach(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100);
p.pixelDensity(1);
};
});
});
- afterEach(function() {
+ afterEach(function () {
myp5.remove();
});
- test('set() works with p5.Graphics on main canvas', function() {
+ test('set() works with p5.Graphics on main canvas', function () {
myp5.background(0, 255, 0);
const gfx = myp5.createGraphics(20, 20);
gfx.background(255, 0, 0);
@@ -267,7 +290,7 @@ suite('Rendering', function() {
assert.deepEqual(myp5.get(5, 5), [0, 255, 0, 255]);
});
- test('set() works with p5.Graphics on p5.Graphics', function() {
+ test('set() works with p5.Graphics on p5.Graphics', function () {
const mainGfx = myp5.createGraphics(100, 100);
mainGfx.background(255);
const smallGfx = myp5.createGraphics(20, 20);
@@ -278,7 +301,7 @@ suite('Rendering', function() {
assert.deepEqual(myp5.get(15, 15), [0, 0, 255, 255]);
});
- test('set() clears area under p5.Graphics', function() {
+ test('set() clears area under p5.Graphics', function () {
myp5.background(255);
myp5.stroke(255, 0, 0);
myp5.strokeWeight(2);
@@ -292,7 +315,7 @@ suite('Rendering', function() {
assert.deepEqual(myp5.get(35, 35), [0, 255, 0, 255]);
});
- test('set() works at edge (0,0)', function() {
+ test('set() works at edge (0,0)', function () {
myp5.background(255);
const gfx = myp5.createGraphics(30, 30);
gfx.background(0, 0, 255);
@@ -302,7 +325,7 @@ suite('Rendering', function() {
assert.deepEqual(myp5.get(35, 35), [255, 255, 255, 255]);
});
- test('set() behaves same for p5.Graphics and p5.Image', function() {
+ test('set() behaves same for p5.Graphics and p5.Image', function () {
const gfx = myp5.createGraphics(20, 20);
gfx.background(255, 0, 0);
const img = gfx.get();
@@ -318,39 +341,38 @@ suite('Rendering', function() {
});
});
- suite('p5.prototype.createGraphics pixelDensity', function() {
+ suite('p5.prototype.createGraphics pixelDensity', function () {
let myp5;
- beforeEach(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {
+ beforeEach(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100);
};
});
});
- afterEach(function() {
+ afterEach(function () {
myp5.remove();
});
- test('createGraphics should inherit pixelDensity from parent sketch', function() {
+ test('createGraphics should inherit pixelDensity from parent sketch', function () {
myp5.pixelDensity(1);
const g = myp5.createGraphics(100, 100);
assert.equal(g.pixelDensity(), 1);
});
- test('createGraphics should inherit non-default pixelDensity', function() {
+ test('createGraphics should inherit non-default pixelDensity', function () {
myp5.pixelDensity(3);
const g = myp5.createGraphics(100, 100);
assert.equal(g.pixelDensity(), 3);
});
- test('createGraphics should use default pixelDensity when not explicitly set', function() {
+ test('createGraphics should use default pixelDensity when not explicitly set', function () {
// When no pixelDensity is set, both should match
const expectedDensity = myp5.pixelDensity();
const g = myp5.createGraphics(100, 100);
assert.equal(g.pixelDensity(), expectedDensity);
});
});
-
});
diff --git a/test/unit/core/sketch_overrides.js b/test/unit/core/sketch_overrides.js
index 3504c00e32..44a045f6a1 100644
--- a/test/unit/core/sketch_overrides.js
+++ b/test/unit/core/sketch_overrides.js
@@ -1,15 +1,13 @@
-import {
- verifierUtils
-} from '../../../src/friendly_errors/sketch_verifier.js';
+import { verifierUtils } from '../../../src/friendly_errors/sketch_verifier.js';
suite('Sketch Verifier', function () {
const mockP5 = {
_validateParameters: vi.fn(),
- Color: function () { },
- Vector: function () { },
+ Color: function () {},
+ Vector: function () {},
prototype: {
- rect: function () { },
- ellipse: function () { }
+ rect: function () {},
+ ellipse: function () {}
}
};
@@ -55,13 +53,13 @@ suite('Sketch Verifier', function () {
`;
- vi.spyOn(document, 'querySelectorAll')
- .mockImplementation((...args) =>
- fakeDocument.querySelectorAll(...args)
- );
+ vi.spyOn(document, 'querySelectorAll').mockImplementation((...args) =>
+ fakeDocument.querySelectorAll(...args)
+ );
- vi.spyOn(verifierUtils, 'fetchScript')
- .mockImplementation(() => Promise.resolve(userCode));
+ vi.spyOn(verifierUtils, 'fetchScript').mockImplementation(() =>
+ Promise.resolve(userCode)
+ );
const result = await verifierUtils.getUserCode();
@@ -84,44 +82,44 @@ suite('Sketch Verifier', function () {
const result = verifierUtils.extractUserDefinedVariablesAndFuncs(code);
const expectedResult = {
- 'functions': [
+ functions: [
{
- 'line': 5,
- 'name': 'foo'
+ line: 5,
+ name: 'foo'
},
{
- 'line': 6,
- 'name': 'bar'
+ line: 6,
+ name: 'bar'
},
{
- 'line': 7,
- 'name': 'baz'
+ line: 7,
+ name: 'baz'
}
],
- 'variables': [
+ variables: [
{
- 'line': 1,
- 'name': 'x'
+ line: 1,
+ name: 'x'
},
{
- 'line': 2,
- 'name': 'y'
+ line: 2,
+ name: 'y'
},
{
- 'line': 3,
- 'name': 'z'
+ line: 3,
+ name: 'z'
},
{
- 'line': 4,
- 'name': 'v1'
+ line: 4,
+ name: 'v1'
},
{
- 'line': 4,
- 'name': 'v2'
+ line: 4,
+ name: 'v2'
},
{
- 'line': 4,
- 'name': 'v3'
+ line: 4,
+ name: 'v3'
}
]
};
@@ -151,23 +149,23 @@ suite('Sketch Verifier', function () {
const result = verifierUtils.extractUserDefinedVariablesAndFuncs(code);
const expectedResult = {
- 'functions': [],
- 'variables': [
+ functions: [],
+ variables: [
{
- 'line': 2,
- 'name': 'x'
+ line: 2,
+ name: 'x'
},
{
- 'line': 6,
- 'name': 'y'
+ line: 6,
+ name: 'y'
},
{
- 'line': 11,
- 'name': 'z'
+ line: 11,
+ name: 'z'
},
{
- 'line': 13,
- 'name': 'i'
+ line: 13,
+ name: 'i'
}
]
};
@@ -177,11 +175,12 @@ suite('Sketch Verifier', function () {
test('Handles parsing errors', function () {
const invalidCode = 'let x = ;';
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
+ const consoleSpy = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => {});
- const result = verifierUtils.extractUserDefinedVariablesAndFuncs(
- invalidCode
- );
+ const result =
+ verifierUtils.extractUserDefinedVariablesAndFuncs(invalidCode);
expect(consoleSpy).toHaveBeenCalled();
expect(result).toEqual({ variables: [], functions: [] });
@@ -200,7 +199,7 @@ suite('Sketch Verifier', function () {
}
beforeEach(function () {
- consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
+ consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
});
afterEach(function () {
diff --git a/test/unit/core/structure.js b/test/unit/core/structure.js
index 8d32128002..9a36e71a13 100644
--- a/test/unit/core/structure.js
+++ b/test/unit/core/structure.js
@@ -1,22 +1,26 @@
import p5 from '../../../src/app.js';
-import { testSketchWithPromise, createP5Iframe, P5_SCRIPT_TAG } from '../../js/p5_helpers';
+import {
+ testSketchWithPromise,
+ createP5Iframe,
+ P5_SCRIPT_TAG
+} from '../../js/p5_helpers';
-suite('Structure', function() {
+suite('Structure', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.prototype.isLooping', function() {
+ suite('p5.prototype.isLooping', function () {
// This suite uses its own p5 instance rather than the shared myp5:
// toggling noLoop()/loop() within the same frame would double-pump
// the shared instance's draw loop (noLoop() doesn't cancel the
@@ -24,39 +28,39 @@ suite('Structure', function() {
// one), breaking the timing-based tests below.
var loopSketch;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
loopSketch = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
loopSketch.remove();
});
- test('isLooping should return true by default', function() {
+ test('isLooping should return true by default', function () {
assert.strictEqual(loopSketch.isLooping(), true);
});
- test('isLooping should return false after noLoop()', function() {
+ test('isLooping should return false after noLoop()', function () {
loopSketch.noLoop();
assert.strictEqual(loopSketch.isLooping(), false);
});
- test('isLooping should return true after loop()', function() {
+ test('isLooping should return true after loop()', function () {
loopSketch.loop();
assert.strictEqual(loopSketch.isLooping(), true);
});
});
- suite('p5.prototype.loop and p5.prototype.noLoop', function() {
- test('noLoop should stop', function() {
- return new Promise(function(resolve, reject) {
+ suite('p5.prototype.loop and p5.prototype.noLoop', function () {
+ test('noLoop should stop', function () {
+ return new Promise(function (resolve, reject) {
var c0 = myp5.frameCount;
myp5.noLoop();
- myp5.draw = function() {
+ myp5.draw = function () {
var c1 = myp5.frameCount;
// Allow one final draw to run
if (c1 > c0 + 1) {
@@ -67,11 +71,11 @@ suite('Structure', function() {
});
});
- test('loop should restart', function() {
- return new Promise(function(resolve, reject) {
+ test('loop should restart', function () {
+ return new Promise(function (resolve, reject) {
var c0 = myp5.frameCount;
myp5.noLoop();
- myp5.draw = function() {
+ myp5.draw = function () {
var c1 = myp5.frameCount;
// Allow one final draw to run
if (c1 > c0 + 1) {
@@ -79,11 +83,11 @@ suite('Structure', function() {
}
};
setTimeout(resolve, 100);
- }).then(function() {
- return new Promise(function(resolve, reject) {
+ }).then(function () {
+ return new Promise(function (resolve, reject) {
myp5.draw = resolve;
myp5.loop();
- setTimeout(function() {
+ setTimeout(function () {
reject('Failed to restart draw.');
}, 100);
});
@@ -91,7 +95,7 @@ suite('Structure', function() {
});
});
- suite('p5.prototype.push and p5.prototype.pop', function() {
+ suite('p5.prototype.push and p5.prototype.pop', function () {
function getRenderState() {
return { ...myp5._renderer.states };
}
@@ -104,146 +108,146 @@ suite('Structure', function() {
assert.deepEqual(getRenderState(), originalState);
}
- test('leak no state after fill()', function() {
+ test('leak no state after fill()', function () {
myp5.noFill();
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.fill('red');
});
});
- test('leak no state after noFill()', function() {
+ test('leak no state after noFill()', function () {
myp5.fill('red');
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.noFill();
});
});
- test('leak no state after stroke()', function() {
+ test('leak no state after stroke()', function () {
myp5.noStroke();
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.stroke('red');
});
});
- test('leak no state after noStroke()', function() {
+ test('leak no state after noStroke()', function () {
myp5.stroke('red');
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.noStroke();
});
});
- test('leak no state after tint()', function() {
+ test('leak no state after tint()', function () {
myp5.noTint();
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.tint(255, 0, 0);
});
});
- test('leak no state after noTint()', function() {
+ test('leak no state after noTint()', function () {
myp5.tint(255, 0, 0);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.noTint();
});
});
- test('leak no state after strokeWeight()', function() {
+ test('leak no state after strokeWeight()', function () {
myp5.strokeWeight(1);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.strokeWeight(10);
});
});
- test('leak no state after strokeCap()', function() {
+ test('leak no state after strokeCap()', function () {
myp5.strokeCap(myp5.ROUND);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.strokeCap(myp5.SQUARE);
});
});
- test('leak no state after strokeJoin()', function() {
+ test('leak no state after strokeJoin()', function () {
myp5.strokeJoin(myp5.BEVEL);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.strokeJoin(myp5.MITER);
});
});
- test('leak no state after imageMode()', function() {
+ test('leak no state after imageMode()', function () {
myp5.imageMode(myp5.CORNER);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.imageMode(myp5.CENTER);
});
});
- test('leak no state after rectMode()', function() {
+ test('leak no state after rectMode()', function () {
myp5.rectMode(myp5.CORNER);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.rectMode(myp5.CENTER);
});
});
- test('leak no state after ellipseMode()', function() {
+ test('leak no state after ellipseMode()', function () {
myp5.ellipseMode(myp5.CORNER);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.ellipseMode(myp5.CENTER);
});
});
- test('leak no state after colorMode()', function() {
+ test('leak no state after colorMode()', function () {
myp5.colorMode(myp5.HSB);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.colorMode(myp5.RGB);
});
});
- test('leak no state after textAlign()', function() {
+ test('leak no state after textAlign()', function () {
myp5.textAlign(myp5.RIGHT, myp5.BOTTOM);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.textAlign(myp5.CENTER, myp5.CENTER);
});
});
- test('leak no state after textFont()', function() {
+ test('leak no state after textFont()', function () {
myp5.textFont('Georgia');
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.textFont('Helvetica');
});
});
- test('leak no state after textStyle()', function() {
+ test('leak no state after textStyle()', function () {
myp5.textStyle(myp5.ITALIC);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.textStyle(myp5.BOLD);
});
});
- test('leak no state after textSize()', function() {
+ test('leak no state after textSize()', function () {
myp5.textSize(12);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.textSize(16);
});
});
- test('leak no state after textLeading()', function() {
+ test('leak no state after textLeading()', function () {
myp5.textLeading(20);
- assertCanPreserveRenderState(function() {
+ assertCanPreserveRenderState(function () {
myp5.textLeading(30);
});
});
});
- suite('p5.prototype.redraw', function() {
+ suite('p5.prototype.redraw', function () {
var iframe;
- afterAll(function() {
+ afterAll(function () {
if (iframe) {
iframe.teardown();
iframe = null;
}
});
- test('resets the rendering matrix between frames', function() {
- return new Promise(function(resolve, reject) {
- myp5.draw = function() {
+ test('resets the rendering matrix between frames', function () {
+ return new Promise(function (resolve, reject) {
+ myp5.draw = function () {
myp5.background(0);
myp5.stroke(255);
myp5.point(10, 10);
@@ -257,16 +261,16 @@ suite('Structure', function() {
});
});
- test.todo('instance redraw is independent of window', function() {
+ test.todo('instance redraw is independent of window', function () {
// callback for p5 instance mode.
// It does not call noLoop so redraw will be called many times.
// Redraw is not supposed to call window.draw even though no draw is defined in cb
function cb(s) {
- s.setup = function() {
+ s.setup = function () {
setTimeout(window.afterSetup, 1000);
};
}
- return new Promise(function(resolve) {
+ return new Promise(function (resolve) {
iframe = createP5Iframe(
[
P5_SCRIPT_TAG,
@@ -279,41 +283,39 @@ suite('Structure', function() {
].join('\n')
);
iframe.elt.contentWindow.afterSetup = resolve;
- }).then(function() {
+ }).then(function () {
var win = iframe.elt.contentWindow;
assert.strictEqual(win.globalDraws, 1);
});
});
});
- suite('loop', function() {
- testSketchWithPromise('loop in setup does not call draw', function(
- sketch,
- resolve,
- reject
- ) {
- sketch.setup = function() {
- sketch.loop();
- resolve();
- };
-
- sketch.draw = function() {
- reject(new Error('Entered draw during loop()'));
- };
- });
+ suite('loop', function () {
+ testSketchWithPromise(
+ 'loop in setup does not call draw',
+ function (sketch, resolve, reject) {
+ sketch.setup = function () {
+ sketch.loop();
+ resolve();
+ };
- testSketchWithPromise('loop in draw does not call draw', function(
- sketch,
- resolve,
- reject
- ) {
- sketch.draw = function() {
- if (sketch.frameCount > 1) {
- reject(new Error('re-entered draw during loop() call'));
- }
- sketch.loop();
- resolve();
- };
- });
+ sketch.draw = function () {
+ reject(new Error('Entered draw during loop()'));
+ };
+ }
+ );
+
+ testSketchWithPromise(
+ 'loop in draw does not call draw',
+ function (sketch, resolve, reject) {
+ sketch.draw = function () {
+ if (sketch.frameCount > 1) {
+ reject(new Error('re-entered draw during loop() call'));
+ }
+ sketch.loop();
+ resolve();
+ };
+ }
+ );
});
});
diff --git a/test/unit/core/transform.js b/test/unit/core/transform.js
index c4b0edbb8e..e016893af3 100644
--- a/test/unit/core/transform.js
+++ b/test/unit/core/transform.js
@@ -1,77 +1,77 @@
import { mockP5, mockP5Prototype } from '../../js/mocks';
import transform from '../../../src/core/transform';
-suite('Transform', function() {
- beforeAll(function() {
+suite('Transform', function () {
+ beforeAll(function () {
transform(mockP5, mockP5Prototype);
});
- suite('p5.prototype.rotate', function() {
- test('should be a function', function() {
+ suite('p5.prototype.rotate', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.rotate);
assert.typeOf(mockP5Prototype.rotate, 'function');
});
});
- suite('p5.prototype.rotateX', function() {
- test('should be a function', function() {
+ suite('p5.prototype.rotateX', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.rotateX);
assert.typeOf(mockP5Prototype.rotateX, 'function');
});
- test('throws error. should be used in WEBGL mode', function() {
- assert.throws(function() {
+ test('throws error. should be used in WEBGL mode', function () {
+ assert.throws(function () {
mockP5Prototype.rotateX(100);
}, Error);
});
});
- suite('p5.prototype.rotateY', function() {
- test('should be a function', function() {
+ suite('p5.prototype.rotateY', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.rotateY);
assert.typeOf(mockP5Prototype.rotateY, 'function');
});
- test('throws error. should be used in WEBGL mode', function() {
- assert.throws(function() {
+ test('throws error. should be used in WEBGL mode', function () {
+ assert.throws(function () {
mockP5Prototype.rotateY(100);
}, Error);
});
});
- suite('p5.prototype.rotateZ', function() {
- test('should be a function', function() {
+ suite('p5.prototype.rotateZ', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.rotateZ);
assert.typeOf(mockP5Prototype.rotateZ, 'function');
});
- test('throws error. should be used in WEBGL mode', function() {
- assert.throws(function() {
+ test('throws error. should be used in WEBGL mode', function () {
+ assert.throws(function () {
mockP5Prototype.rotateZ(100);
}, Error);
});
});
- suite('p5.prototype.scale', function() {
- test('should be a function', function() {
+ suite('p5.prototype.scale', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.scale);
assert.typeOf(mockP5Prototype.scale, 'function');
});
});
- suite('p5.prototype.shearX', function() {
- test('should be a function', function() {
+ suite('p5.prototype.shearX', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.shearX);
assert.typeOf(mockP5Prototype.shearX, 'function');
});
});
- suite('p5.prototype.shearY', function() {
- test('should be a function', function() {
+ suite('p5.prototype.shearY', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.shearY);
assert.typeOf(mockP5Prototype.shearY, 'function');
});
});
- suite('p5.prototype.translate', function() {
- test('should be a function', function() {
+ suite('p5.prototype.translate', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.translate);
assert.typeOf(mockP5Prototype.translate, 'function');
});
diff --git a/test/unit/core/version.js b/test/unit/core/version.js
index a69d33e654..0530a20bca 100644
--- a/test/unit/core/version.js
+++ b/test/unit/core/version.js
@@ -1,27 +1,27 @@
import p5 from '../../../src/app.js';
-suite('Version', function() {
+suite('Version', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- test('exists on p5 object', function() {
+ test('exists on p5 object', function () {
assert.isString(p5.VERSION);
// ensure the string isn't empty
assert.isTrue(p5.VERSION.length > 0);
});
- test('exists on instance of p5 sketch', function() {
+ test('exists on instance of p5 sketch', function () {
assert.isString(myp5.VERSION);
// ensure the string isn't empty
assert.isTrue(myp5.VERSION.length > 0);
diff --git a/test/unit/core/vertex.js b/test/unit/core/vertex.js
index a9059fe124..f2403a4d08 100644
--- a/test/unit/core/vertex.js
+++ b/test/unit/core/vertex.js
@@ -1,59 +1,59 @@
import p5 from '../../../src/app.js';
import { vi } from 'vitest';
-suite('Vertex', function() {
+suite('Vertex', function () {
var myp5;
- beforeEach(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeEach(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterEach(function() {
+ afterEach(function () {
vi.restoreAllMocks();
myp5.remove();
});
- suite('p5.prototype.beginShape', function() {
- test('should be a function', function() {
+ suite('p5.prototype.beginShape', function () {
+ test('should be a function', function () {
assert.ok(myp5.beginShape);
assert.typeOf(myp5.beginShape, 'function');
});
});
- suite('p5.prototype.bezierVertex', function() {
- test('should be a function', function() {
+ suite('p5.prototype.bezierVertex', function () {
+ test('should be a function', function () {
assert.ok(myp5.bezierVertex);
assert.typeOf(myp5.bezierVertex, 'function');
});
});
- suite('p5.prototype.splineVertex', function() {
- test('should be a function', function() {
+ suite('p5.prototype.splineVertex', function () {
+ test('should be a function', function () {
assert.ok(myp5.splineVertex);
assert.typeOf(myp5.splineVertex, 'function');
});
});
- suite('p5.prototype.endShape', function() {
- test('should be a function', function() {
+ suite('p5.prototype.endShape', function () {
+ test('should be a function', function () {
assert.ok(myp5.endShape);
assert.typeOf(myp5.endShape, 'function');
});
});
- suite('p5.prototype.vertex', function() {
- test('should be a function', function() {
+ suite('p5.prototype.vertex', function () {
+ test('should be a function', function () {
assert.ok(myp5.vertex);
assert.typeOf(myp5.vertex, 'function');
});
});
- suite('path segment batching', function() {
- test('consecutive line vertices batch into one segment', function() {
+ suite('path segment batching', function () {
+ test('consecutive line vertices batch into one segment', function () {
myp5.createCanvas(50, 50);
myp5.beginShape();
for (let i = 0; i < 5; i++) {
@@ -66,7 +66,7 @@ suite('Vertex', function() {
myp5.endShape();
});
- test('endShape(CLOSE) keeps the closing vertex in its own segment', function() {
+ test('endShape(CLOSE) keeps the closing vertex in its own segment', function () {
myp5.createCanvas(50, 50);
myp5.beginShape();
myp5.vertex(0, 0);
@@ -82,7 +82,7 @@ suite('Vertex', function() {
assert.isTrue(primitives[2].isClosing);
});
- test('line vertices after a spline segment start a new segment', function() {
+ test('line vertices after a spline segment start a new segment', function () {
myp5.createCanvas(50, 50);
myp5.beginShape();
myp5.vertex(0, 0);
@@ -96,7 +96,7 @@ suite('Vertex', function() {
myp5.endShape();
});
- test('beginContour() batches independently per contour', function() {
+ test('beginContour() batches independently per contour', function () {
myp5.createCanvas(50, 50);
myp5.beginShape();
myp5.vertex(0, 0);
@@ -116,7 +116,7 @@ suite('Vertex', function() {
myp5.endShape();
});
- test('non-PATH shapes keep using primitive capacity', function() {
+ test('non-PATH shapes keep using primitive capacity', function () {
myp5.createCanvas(50, 50);
myp5.beginShape(myp5.TRIANGLES);
for (let i = 0; i < 6; i++) {
diff --git a/test/unit/data/local_storage.js b/test/unit/data/local_storage.js
index 6c8719c777..1b7dc71fa6 100644
--- a/test/unit/data/local_storage.js
+++ b/test/unit/data/local_storage.js
@@ -5,7 +5,7 @@ import creatingReading from '../../../src/color/creating_reading';
import p5Vector from '../../../src/math/p5.Vector';
import math from '../../../src/math/math';
-suite('local storage', function() {
+suite('local storage', function () {
const myBoolean = false;
const myObject = { one: 1, two: { nested: true } };
const myNumber = 46;
@@ -15,7 +15,7 @@ suite('local storage', function() {
const hardCodedTypeID = 'p5TypeID';
- beforeAll(function() {
+ beforeAll(function () {
storage(mockP5, mockP5Prototype);
p5Color(mockP5, mockP5Prototype, {});
creatingReading(mockP5, mockP5Prototype);
@@ -33,78 +33,82 @@ suite('local storage', function() {
mockP5Prototype.storeItem('myVector', myVector);
});
- suite('all keys and type keys should exist in local storage', function() {
- test('boolean storage retrieval should work', function() {
+ suite('all keys and type keys should exist in local storage', function () {
+ test('boolean storage retrieval should work', function () {
assert.equal(mockP5Prototype.getItem('myBoolean'), false);
});
- test('boolean storage should store the correct type ID', function() {
+ test('boolean storage should store the correct type ID', function () {
assert.equal(
- localStorage.getItem('myBoolean' + hardCodedTypeID), 'boolean'
+ localStorage.getItem('myBoolean' + hardCodedTypeID),
+ 'boolean'
);
});
- test('object storage should work', function() {
+ test('object storage should work', function () {
assert.deepEqual(mockP5Prototype.getItem('myObject'), {
one: 1,
two: { nested: true }
});
});
- test('object storage retrieval should store the correct type ID', function() {
+ test('object storage retrieval should store the correct type ID', function () {
assert.equal(
- localStorage.getItem('myObject' + hardCodedTypeID), 'object'
+ localStorage.getItem('myObject' + hardCodedTypeID),
+ 'object'
);
});
- test('number storage retrieval should work', function() {
+ test('number storage retrieval should work', function () {
assert.equal(mockP5Prototype.getItem('myNumber'), 46);
});
- test('number storage should store the correct type ID', function() {
+ test('number storage should store the correct type ID', function () {
assert.equal(
- localStorage.getItem('myNumber' + hardCodedTypeID), 'number'
+ localStorage.getItem('myNumber' + hardCodedTypeID),
+ 'number'
);
});
- test('string storage retrieval should work', function() {
+ test('string storage retrieval should work', function () {
assert.equal(mockP5Prototype.getItem('myString'), 'coolio');
});
- test('string storage should store the correct type ID', function() {
+ test('string storage should store the correct type ID', function () {
assert.equal(
- localStorage.getItem('myString' + hardCodedTypeID), 'string'
+ localStorage.getItem('myString' + hardCodedTypeID),
+ 'string'
);
});
- test('p5 Color should retrieve as p5 Color', function() {
+ test('p5 Color should retrieve as p5 Color', function () {
assert.instanceOf(mockP5Prototype.getItem('myColor'), mockP5.Color);
});
- test('p5 Vector should retrieve as p5 Vector', function() {
+ test('p5 Vector should retrieve as p5 Vector', function () {
assert.instanceOf(mockP5Prototype.getItem('myVector'), mockP5.Vector);
});
});
- var checkRemoval = function(key) {
+ var checkRemoval = function (key) {
mockP5Prototype.removeItem(key);
assert.deepEqual(mockP5Prototype.getItem(key), null);
assert.deepEqual(mockP5Prototype.getItem(key + hardCodedTypeID), null);
};
- suite('should be able to remove all items', function() {
- test('boolean should be removable', function() {
+ suite('should be able to remove all items', function () {
+ test('boolean should be removable', function () {
checkRemoval('myBoolean');
});
- test('number should be removable', function() {
+ test('number should be removable', function () {
checkRemoval('myNumber');
});
- test('object should be removable', function() {
+ test('object should be removable', function () {
checkRemoval('myObject');
});
- test('string should be removable', function() {
+ test('string should be removable', function () {
checkRemoval('myString');
});
- test('color should be removable', function() {
+ test('color should be removable', function () {
checkRemoval('myColor');
});
- test('vector should be removable', function() {
+ test('vector should be removable', function () {
checkRemoval('myVector');
});
});
diff --git a/test/unit/dom/dom.js b/test/unit/dom/dom.js
index 2621ac19b7..4469096e84 100644
--- a/test/unit/dom/dom.js
+++ b/test/unit/dom/dom.js
@@ -6,7 +6,7 @@ import { Element } from '../../../src/dom/p5.Element';
import creatingReading from '../../../src/color/creating_reading';
import p5Color from '../../../src/color/p5.Color';
-suite('DOM', function() {
+suite('DOM', function () {
beforeAll(() => {
dom(mockP5, mockP5Prototype);
creatingReading(mockP5, mockP5Prototype);
@@ -14,7 +14,7 @@ suite('DOM', function() {
});
// Selectors
- suite('p5.prototype.select', function() {
+ suite('p5.prototype.select', function () {
let myp5Container;
const generateButton = (name, className = null) => {
@@ -36,16 +36,16 @@ suite('DOM', function() {
return div;
};
- beforeEach(function() {
+ beforeEach(function () {
myp5Container = document.createElement('div');
document.body.appendChild(myp5Container);
});
- afterEach(function() {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should return only one p5.element if match is found', function() {
+ test('should return only one p5.element if match is found', function () {
// Adding 2 buttons to container
mockP5Prototype.createCheckbox('Text 1');
mockP5Prototype.createCheckbox('Text 2');
@@ -54,7 +54,7 @@ suite('DOM', function() {
assert.instanceOf(result, Element);
});
- test('should find element by class name', function() {
+ test('should find element by class name', function () {
// Creates 2 buttons with same class and test if it selects only one.
const testClassName = 'test-button';
const testButton = generateButton('Button 1', testClassName);
@@ -64,7 +64,7 @@ suite('DOM', function() {
assert.deepEqual(result.elt, testButton.elt);
});
- test('should find element by class name from given container', function() {
+ test('should find element by class name from given container', function () {
// Creates 2 buttons with same class in a container and test if it selects only one.
const testClassName = 'test-button';
generateButton('Button 1', testClassName);
@@ -76,7 +76,7 @@ suite('DOM', function() {
assert.deepEqual(testButton.elt, result.elt);
});
- test('should return null when no matches are found by class name', function() {
+ test('should return null when no matches are found by class name', function () {
// Gives unused className and tests if it returns null
const testClassName = 'test-button';
generateButton('Test Button', testClassName);
@@ -84,7 +84,7 @@ suite('DOM', function() {
assert.isNull(result);
});
- test('should find element by tag name', function() {
+ test('should find element by tag name', function () {
// Creates 2 similar elements and tests if it selects only one.
const testButton = generateButton('Button 1');
generateButton('Button 2');
@@ -92,7 +92,7 @@ suite('DOM', function() {
assert.deepEqual(result.elt, testButton.elt);
});
- test('should find element by tag name from given container', function() {
+ test('should find element by tag name from given container', function () {
// Creates 2 elements inside and outside of a container and tests if it
// selects inside the container
generateButton('Button 1');
@@ -104,13 +104,13 @@ suite('DOM', function() {
assert.deepEqual(result.elt, testButton.elt);
});
- test('should return null when no matches are found by tag name', function() {
+ test('should return null when no matches are found by tag name', function () {
generateButton('Test Button');
const result = mockP5Prototype.select('video', myp5Container);
assert.isNull(result);
});
- test('should select element in container using CSS selector with ID', function() {
+ test('should select element in container using CSS selector with ID', function () {
const divID = 'divId';
const testDiv = generateDiv(divID);
const testButton = generateButton('Button 1');
@@ -121,7 +121,7 @@ suite('DOM', function() {
assert.deepEqual(result.elt, testButton.elt);
});
- test('should select element in container using CSS selector with class name', function() {
+ test('should select element in container using CSS selector with class name', function () {
const divClass = 'divClass';
const testDiv = generateDiv(null, divClass);
const testButton = generateButton('Button 1');
@@ -133,8 +133,8 @@ suite('DOM', function() {
});
});
- suite('p5.prototype.selectAll', function() {
- beforeEach(function() {
+ suite('p5.prototype.selectAll', function () {
+ beforeEach(function () {
const mydiv = document.createElement('div');
mydiv.setAttribute('id', 'main');
@@ -148,16 +148,16 @@ suite('DOM', function() {
document.body.append(mydiv, otherbutton);
});
- afterEach(function() {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should return an array', function() {
+ test('should return an array', function () {
const elements = mockP5Prototype.selectAll('button');
assert.isArray(elements);
});
- test('should return empty array when no matching classes are found', function() {
+ test('should return empty array when no matching classes are found', function () {
const elements = mockP5Prototype.selectAll('.randomElements');
assert.isArray(elements);
assert.lengthOf(elements, 0);
@@ -170,14 +170,14 @@ suite('DOM', function() {
}
};
- test('should find all elements with matching class name', function() {
+ test('should find all elements with matching class name', function () {
const testClassName = 'p5button';
const p5Results = mockP5Prototype.selectAll(`.${testClassName}`);
const domResults = document.getElementsByClassName(testClassName);
matchResults(p5Results, domResults);
});
- test('should find all elements with matching class name in given container', function() {
+ test('should find all elements with matching class name in given container', function () {
const testClassName = 'p5button';
const parentContainerId = 'main';
const p5Results = mockP5Prototype.selectAll(
@@ -189,26 +189,31 @@ suite('DOM', function() {
matchResults(p5Results, domResults);
});
- test('should find all elements with matching tag name', function() {
+ test('should find all elements with matching tag name', function () {
const testTagName = 'button';
const p5Results = mockP5Prototype.selectAll(testTagName);
const domResults = document.getElementsByTagName(testTagName);
matchResults(p5Results, domResults);
});
- test('should find all elements with matching tag name in given container', function() {
+ test('should find all elements with matching tag name in given container', function () {
const testTagName = 'button';
const parentContainerId = 'main';
- const p5Results = mockP5Prototype.selectAll(testTagName, `#${parentContainerId}`);
+ const p5Results = mockP5Prototype.selectAll(
+ testTagName,
+ `#${parentContainerId}`
+ );
const containerElement = document.getElementById(parentContainerId);
const domResults = containerElement.getElementsByTagName(testTagName);
matchResults(p5Results, domResults);
});
- test('should find all elements in container using CSS selector with id', function() {
+ test('should find all elements in container using CSS selector with id', function () {
const testTagName = 'button';
const parentContainerId = 'main';
- const p5Results = mockP5Prototype.selectAll(`#${parentContainerId} ${testTagName}`);
+ const p5Results = mockP5Prototype.selectAll(
+ `#${parentContainerId} ${testTagName}`
+ );
const containerElement = document.getElementById(parentContainerId);
const domResults = containerElement.getElementsByTagName(testTagName);
matchResults(p5Results, domResults);
@@ -216,11 +221,11 @@ suite('DOM', function() {
});
// Events
- suite.todo('p5.Element.prototype.changed', function() {
+ suite.todo('p5.Element.prototype.changed', function () {
testSketchWithPromise(
'should trigger callback when element changes',
- function(sketch, resolve, reject) {
- sketch.setup = function() {
+ function (sketch, resolve, reject) {
+ sketch.setup = function () {
const testElement = sketch.createSlider(0, 100, 50, 10);
testElement.changed(resolve);
testElement.elt.dispatchEvent(new Event('change'));
@@ -230,8 +235,8 @@ suite('DOM', function() {
testSketchWithPromise(
'should not trigger callback after changed(false) is called',
- function(sketch, resolve, reject) {
- sketch.setup = function() {
+ function (sketch, resolve, reject) {
+ sketch.setup = function () {
const testElement = sketch.createSlider(0, 100, 50, 10);
// if callback is called, there is some error. so reject
testElement.changed(reject);
@@ -243,11 +248,11 @@ suite('DOM', function() {
);
});
- suite.todo('p5.Element.prototype.input', function() {
+ suite.todo('p5.Element.prototype.input', function () {
testSketchWithPromise(
'should trigger callback when input is provided',
- function(sketch, resolve, reject) {
- sketch.setup = function() {
+ function (sketch, resolve, reject) {
+ sketch.setup = function () {
const testElement = sketch.createElement('input');
testElement.input(resolve);
testElement.elt.dispatchEvent(new Event('input'));
@@ -257,8 +262,8 @@ suite('DOM', function() {
testSketchWithPromise(
'should not trigger callback after input(false) is called',
- function(sketch, resolve, reject) {
- sketch.setup = function() {
+ function (sketch, resolve, reject) {
+ sketch.setup = function () {
const testElement = sketch.createElement('input');
// if callback is called, there is some error. so reject
testElement.input(reject);
@@ -270,138 +275,137 @@ suite('DOM', function() {
);
});
- suite.todo('p5.prototype.drop', function() {
- testSketchWithPromise('drop fires multiple events', function(
- sketch,
- resolve,
- reject
- ) {
- let testElement;
- let fileFnCounter = 0;
- let eventFnCounter = 0;
- sketch.setup = function() {
- testElement = sketch.createDiv('Drop files inside');
-
- // Setup test functions and constants
- const file1 = new File(['foo'], 'foo.txt', { type: 'text/plain' });
- const file2 = new File(['foo'], 'foo.txt', { type: 'text/plain' });
- const hasFinished = () => {
- if (fileFnCounter > 1 && eventFnCounter === 1) resolve();
- };
- const testFileFn = () => {
- fileFnCounter += 1;
- hasFinished();
- };
- const testEventFn = () => {
- eventFnCounter += 1;
- hasFinished();
+ suite.todo('p5.prototype.drop', function () {
+ testSketchWithPromise(
+ 'drop fires multiple events',
+ function (sketch, resolve, reject) {
+ let testElement;
+ let fileFnCounter = 0;
+ let eventFnCounter = 0;
+ sketch.setup = function () {
+ testElement = sketch.createDiv('Drop files inside');
+
+ // Setup test functions and constants
+ const file1 = new File(['foo'], 'foo.txt', { type: 'text/plain' });
+ const file2 = new File(['foo'], 'foo.txt', { type: 'text/plain' });
+ const hasFinished = () => {
+ if (fileFnCounter > 1 && eventFnCounter === 1) resolve();
+ };
+ const testFileFn = () => {
+ fileFnCounter += 1;
+ hasFinished();
+ };
+ const testEventFn = () => {
+ eventFnCounter += 1;
+ hasFinished();
+ };
+ testElement.drop(testFileFn, testEventFn);
+
+ // Fire a mock drop and test the method
+ const mockedEvent = new Event('drop');
+ mockedEvent.dataTransfer = { files: [file1, file2] };
+ testElement.elt.dispatchEvent(mockedEvent);
};
- testElement.drop(testFileFn, testEventFn);
-
- // Fire a mock drop and test the method
- const mockedEvent = new Event('drop');
- mockedEvent.dataTransfer = { files: [file1, file2] };
- testElement.elt.dispatchEvent(mockedEvent);
- };
- });
+ }
+ );
});
// Add/remove elements
- suite('p5.prototype.createDiv', function() {
- afterEach(function() {
+ suite('p5.prototype.createDiv', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createDiv);
});
- test('should return a p5.Element of div type', function() {
+ test('should return a p5.Element of div type', function () {
const testElement = mockP5Prototype.createDiv();
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLDivElement);
});
- test('should set given param as innerHTML of div', function() {
+ test('should set given param as innerHTML of div', function () {
const testHTML = 'Hello
';
const testElement = mockP5Prototype.createDiv(testHTML);
assert.deepEqual(testElement.elt.innerHTML, testHTML);
});
});
- suite('p5.prototype.createP', function() {
- afterEach(function() {
+ suite('p5.prototype.createP', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createP);
});
- test('should return a p5.Element of p type', function() {
+ test('should return a p5.Element of p type', function () {
const testElement = mockP5Prototype.createP();
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLParagraphElement);
});
- test('should set given param as innerHTML of p', function() {
+ test('should set given param as innerHTML of p', function () {
const testHTML = 'Hello';
const testElement = mockP5Prototype.createP(testHTML);
assert.deepEqual(testElement.elt.innerHTML, testHTML);
});
});
- suite('p5.prototype.createSpan', function() {
- afterEach(function() {
+ suite('p5.prototype.createSpan', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createSpan);
});
- test('should return a p5.Element of span type', function() {
+ test('should return a p5.Element of span type', function () {
const testElement = mockP5Prototype.createSpan();
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLSpanElement);
});
- test('should set given param as innerHTML of span', function() {
+ test('should set given param as innerHTML of span', function () {
const testHTML = 'Hello';
const testElement = mockP5Prototype.createSpan(testHTML);
assert.deepEqual(testElement.elt.innerHTML, testHTML);
});
});
- suite('p5.prototype.createImg', function() {
+ suite('p5.prototype.createImg', function () {
const imagePath = '/test/unit/assets/cat.jpg';
- afterEach(function() {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createImg);
});
- test('should return p5.Element of image type', function() {
+ test('should return p5.Element of image type', function () {
const testElement = mockP5Prototype.createImg(imagePath, '');
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLImageElement);
});
- test('should set src of image from params', function() {
+ test('should set src of image from params', function () {
const testElement = mockP5Prototype.createImg(imagePath, '');
assert.isTrue(testElement.elt.src.endsWith(imagePath));
});
- test('should set alt from params if given', function() {
+ test('should set alt from params if given', function () {
const alternativeText = 'Picture of a cat';
const testElement = mockP5Prototype.createImg(imagePath, alternativeText);
assert.deepEqual(testElement.elt.alt, alternativeText);
});
- test('should set crossOrigin from params if given', function() {
+ test('should set crossOrigin from params if given', function () {
const crossOrigin = 'anonymous';
const testElement = mockP5Prototype.createImg(imagePath, '', crossOrigin);
assert.deepEqual(testElement.elt.crossOrigin, crossOrigin);
@@ -418,8 +422,8 @@ suite('DOM', function() {
// );
});
- suite('p5.prototype.createA', function() {
- afterEach(function() {
+ suite('p5.prototype.createA', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -429,7 +433,7 @@ suite('DOM', function() {
assert.instanceOf(testElement.elt, HTMLAnchorElement);
});
- test('creates anchor with given link & text', function() {
+ test('creates anchor with given link & text', function () {
const testUrl = 'https://p5js.org/';
const testText = 'p5js website';
const testElement = mockP5Prototype.createA(testUrl, testText);
@@ -438,15 +442,19 @@ suite('DOM', function() {
assert.deepEqual(testElement.elt.text, testText);
});
- test('creates anchor with given target', function() {
+ test('creates anchor with given target', function () {
const testTarget = 'blank';
- const testElement = mockP5Prototype.createA('https://p5js.org', 'p5js website', testTarget);
+ const testElement = mockP5Prototype.createA(
+ 'https://p5js.org',
+ 'p5js website',
+ testTarget
+ );
assert.deepEqual(testElement.elt.target, testTarget);
});
});
- suite('p5.prototype.createSlider', function() {
- afterEach(function() {
+ suite('p5.prototype.createSlider', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -457,30 +465,33 @@ suite('DOM', function() {
assert.deepEqual(testElement.elt.type, 'range');
});
- test('should set min and max values', function() {
+ test('should set min and max values', function () {
const testElement = mockP5Prototype.createSlider(20, 80);
assert.deepEqual(testElement.elt.min, '20');
assert.deepEqual(testElement.elt.max, '80');
});
- test('should set slider position', function() {
+ test('should set slider position', function () {
const testElement = mockP5Prototype.createSlider(20, 80, 50);
assert.deepEqual(testElement.elt.value, '50');
});
- test('should set step value', function() {
+ test('should set step value', function () {
const testElement = mockP5Prototype.createSlider(20, 80, 10, 5);
assert.deepEqual(testElement.elt.step, '5');
});
});
- suite('p5.prototype.createButton', function() {
- afterEach(function() {
+ suite('p5.prototype.createButton', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should return a p5.Element of button type', function() {
- const testElement = mockP5Prototype.createButton('testButton', 'testButton');
+ test('should return a p5.Element of button type', function () {
+ const testElement = mockP5Prototype.createButton(
+ 'testButton',
+ 'testButton'
+ );
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLButtonElement);
});
@@ -497,8 +508,8 @@ suite('DOM', function() {
// );
});
- suite('p5.prototype.createCheckbox', function() {
- afterEach(function() {
+ suite('p5.prototype.createCheckbox', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -513,11 +524,11 @@ suite('DOM', function() {
? el.elt.firstElementChild.getElementsByTagName('input')[0]
: null;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createCheckbox);
});
- test('should return a p5.Element with checkbox as descendant', function() {
+ test('should return a p5.Element with checkbox as descendant', function () {
const testElement = mockP5Prototype.createCheckbox();
const checkboxElement = getCheckboxElement(testElement);
@@ -525,7 +536,7 @@ suite('DOM', function() {
assert.instanceOf(checkboxElement, HTMLInputElement);
});
- test('calling createCheckbox(label) should create checkbox and set its label', function() {
+ test('calling createCheckbox(label) should create checkbox and set its label', function () {
const labelValue = 'label';
const testElement = mockP5Prototype.createCheckbox(labelValue);
const spanElement = getSpanElement(testElement);
@@ -538,7 +549,7 @@ suite('DOM', function() {
assert.deepEqual(testElementLabelValue, labelValue);
});
- test('calling createCheckbox(label, true) should create a checked checkbox and set its label', function() {
+ test('calling createCheckbox(label, true) should create a checked checkbox and set its label', function () {
const labelValue = 'label';
const testElement = mockP5Prototype.createCheckbox(labelValue, true);
@@ -553,20 +564,20 @@ suite('DOM', function() {
assert.isTrue(testElement.checked());
});
- test('calling checked() should return checked value of checkbox', function() {
+ test('calling checked() should return checked value of checkbox', function () {
const testElement = mockP5Prototype.createCheckbox('', true);
assert.isTrue(testElement.checked());
});
- test('calling checked(true) should set checked value of checkbox', function() {
+ test('calling checked(true) should set checked value of checkbox', function () {
const testElement = mockP5Prototype.createCheckbox('', false);
testElement.checked(true);
assert.isTrue(testElement.checked());
});
});
- suite('p5.prototype.createSelect', function() {
- afterEach(function() {
+ suite('p5.prototype.createSelect', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -581,23 +592,23 @@ suite('DOM', function() {
return selectElement;
};
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createSelect);
});
- test('should return p5.Element of select HTML Element', function() {
+ test('should return p5.Element of select HTML Element', function () {
const testElement = mockP5Prototype.createSelect();
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLSelectElement);
});
- test('should return p5.Element when select element is passed', function() {
+ test('should return p5.Element when select element is passed', function () {
const selectElement = createHTMLSelect(['option1', 'option2']);
const testElement = mockP5Prototype.createSelect(selectElement);
assert.deepEqual(testElement.elt, selectElement);
});
- test('calling option(newName) should add a new option', function() {
+ test('calling option(newName) should add a new option', function () {
const testOptions = ['John', 'Bethany', 'Dwayne'];
const testElement = mockP5Prototype.createSelect();
for (const optionName of testOptions) testElement.option(optionName);
@@ -610,7 +621,7 @@ suite('DOM', function() {
assert.deepEqual(htmlOptions, testOptions);
});
- test('calling option(name, newValue) should update value of option', function() {
+ test('calling option(name, newValue) should update value of option', function () {
const optionName = 'john';
const testValues = [1, 'abc', true];
const testElement = mockP5Prototype.createSelect();
@@ -623,7 +634,7 @@ suite('DOM', function() {
}
});
- test('calling value() should return current selected option', function() {
+ test('calling value() should return current selected option', function () {
const testElement = mockP5Prototype.createSelect();
testElement.option('Sunday');
testElement.option('Monday');
@@ -631,7 +642,7 @@ suite('DOM', function() {
assert(testElement.value(), 'Monday');
});
- test('calling selected() should return all selected options', function() {
+ test('calling selected() should return all selected options', function () {
const testElement = mockP5Prototype.createSelect(true);
const options = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
for (const optionName of options) testElement.option(optionName);
@@ -647,7 +658,7 @@ suite('DOM', function() {
}
});
- test('should update select value when HTML special characters are in the name', function() {
+ test('should update select value when HTML special characters are in the name', function () {
const testElement = mockP5Prototype.createSelect(true);
testElement.option('&', 'foo');
assert.equal(testElement.elt.options.length, 1);
@@ -656,7 +667,7 @@ suite('DOM', function() {
assert.equal(testElement.elt.options[0].value, 'bar');
});
- test('calling selected(value) should updated selectedIndex', function() {
+ test('calling selected(value) should updated selectedIndex', function () {
const testElement = mockP5Prototype.createSelect(true);
const options = ['Sunday', 'Monday', 'Tuesday', 'Friday'];
for (const optionName of options) testElement.option(optionName);
@@ -670,14 +681,14 @@ suite('DOM', function() {
}
});
- test('calling disable() should disable the whole dropdown', function() {
+ test('calling disable() should disable the whole dropdown', function () {
const testElement = mockP5Prototype.createSelect(true);
testElement.disable();
assert.isTrue(testElement.elt.disabled);
});
- test('should disable an option when disable() method invoked with option name', function() {
+ test('should disable an option when disable() method invoked with option name', function () {
const testElement = mockP5Prototype.createSelect(true);
const options = ['Sunday', 'Monday', 'Tuesday', 'Friday'];
for (const optionName of options) testElement.option(optionName);
@@ -689,8 +700,8 @@ suite('DOM', function() {
});
});
- suite('p5.prototype.createRadio', function() {
- afterEach(function() {
+ suite('p5.prototype.createRadio', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -719,17 +730,17 @@ suite('DOM', function() {
)
.map(el => (isRadioInput(el) ? el : el.firstElementChild));
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createRadio);
});
- test('should return p5.Element of radio type', function() {
+ test('should return p5.Element of radio type', function () {
const testElement = mockP5Prototype.createRadio();
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLDivElement);
});
- test('should return p5.Element from existing radio Element', function() {
+ test('should return p5.Element from existing radio Element', function () {
const options = ['Saturday', 'Sunday'];
const radioElement = createRadioElement(options);
const testElement = mockP5Prototype.createRadio(radioElement);
@@ -737,7 +748,7 @@ suite('DOM', function() {
assert.deepEqual(testElement.elt, radioElement);
});
- test('calling option(value) should return existing radio element', function() {
+ test('calling option(value) should return existing radio element', function () {
const options = ['Saturday', 'Sunday'];
const radioElement = createRadioElement(options);
const testElement = mockP5Prototype.createRadio(radioElement);
@@ -748,7 +759,7 @@ suite('DOM', function() {
assert.deepEqual(getChildren(testElement.elt).length, options.length);
});
- test('calling option(newValue) should create a new radio input', function() {
+ test('calling option(newValue) should create a new radio input', function () {
const testName = 'defaultRadio';
const options = ['Saturday', 'Sunday'];
const testElement = mockP5Prototype.createRadio(testName);
@@ -766,7 +777,7 @@ suite('DOM', function() {
}
});
- test('calling option(value, label) should set label of option', function() {
+ test('calling option(value, label) should set label of option', function () {
const testName = 'defaultRadio';
const options = ['Saturday', 'Sunday'];
const testElement = mockP5Prototype.createRadio(testName);
@@ -780,7 +791,7 @@ suite('DOM', function() {
}
});
- test('should use given name for all options', function() {
+ test('should use given name for all options', function () {
const testName = 'defaultRadio';
const options = ['Saturday', 'Sunday'];
const radioElement = createRadioElement(options);
@@ -792,7 +803,7 @@ suite('DOM', function() {
}
});
- test('calling remove(value) should remove option', function() {
+ test('calling remove(value) should remove option', function () {
const options = ['Monday', 'Friday', 'Saturday', 'Sunday'];
const radioElement = createRadioElement(options);
const testElement = mockP5Prototype.createRadio(radioElement);
@@ -808,7 +819,7 @@ suite('DOM', function() {
assert.deepEqual(options, remainingOptions);
});
- test('calling value() should return selected value', function() {
+ test('calling value() should return selected value', function () {
const options = ['Monday', 'Friday', 'Saturday', 'Sunday'];
const selectedValue = options[1];
const testElement = mockP5Prototype.createRadio();
@@ -817,7 +828,7 @@ suite('DOM', function() {
assert.deepEqual(testElement.value(), selectedValue);
});
- test('calling selected(value) should select a value and return it', function() {
+ test('calling selected(value) should select a value and return it', function () {
const options = ['Monday', 'Friday', 'Saturday', 'Sunday'];
const testElement = mockP5Prototype.createRadio();
for (const option of options) testElement.option(option);
@@ -829,7 +840,7 @@ suite('DOM', function() {
}
});
- test('calling selected() should return the currently selected option', function() {
+ test('calling selected() should return the currently selected option', function () {
const options = ['Monday', 'Friday', 'Saturday', 'Sunday'];
const testElement = mockP5Prototype.createRadio();
for (const option of options) testElement.option(option);
@@ -841,7 +852,7 @@ suite('DOM', function() {
assert.isTrue(selectedOption.checked);
});
- test('calling disable() should disable all the radio inputs', function() {
+ test('calling disable() should disable all the radio inputs', function () {
const options = ['Monday', 'Friday', 'Saturday', 'Sunday'];
const testElement = mockP5Prototype.createRadio();
for (const option of options) testElement.option(option);
@@ -853,16 +864,16 @@ suite('DOM', function() {
});
});
- suite('p5.prototype.createColorPicker', function() {
- afterEach(function() {
+ suite('p5.prototype.createColorPicker', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createColorPicker);
});
- test('should return p5.Element of input[color] type', function() {
+ test('should return p5.Element of input[color] type', function () {
const testElement = mockP5Prototype.createColorPicker();
assert.instanceOf(testElement, Element);
@@ -871,49 +882,49 @@ suite('DOM', function() {
});
// TODO: pending finalization of p5.Color implementation
- test.todo('should accept a p5.Color as param', function() {
+ test.todo('should accept a p5.Color as param', function () {
const testColor = mockP5Prototype.color('red');
const testElement = mockP5Prototype.createColorPicker(testColor);
assert.deepEqual(testElement.elt.value, testColor.toString('#rrggbb'));
});
- test.todo('should accept a string as param', function() {
+ test.todo('should accept a string as param', function () {
const testColorString = '#f00f00';
const testElement = mockP5Prototype.createColorPicker(testColorString);
assert.deepEqual(testElement.elt.value, testColorString);
});
- test.todo('calling color() should return the current color as p5.color', function() {
+ test.todo('calling color() should return the current color as p5.color', function () {
const testColorString = 'red';
const testColor = mockP5Prototype.color(testColorString);
const testElement = mockP5Prototype.createColorPicker(testColorString);
assert.deepEqual(testElement.color(), testColor);
});
- test.todo('calling value() should return hex string of color', function() {
+ test.todo('calling value() should return hex string of color', function () {
const testColor = mockP5Prototype.color('aqua');
const testElement = mockP5Prototype.createColorPicker(testColor);
assert.deepEqual(testElement.value(), testColor.toString('#rrggbb'));
});
});
- suite('p5.prototype.createInput', function() {
- afterEach(function() {
+ suite('p5.prototype.createInput', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createInput);
});
- test('should return p5.Element of input type', function() {
+ test('should return p5.Element of input type', function () {
const testElement = mockP5Prototype.createInput();
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLInputElement);
});
- test('should set given value as input', function() {
+ test('should set given value as input', function () {
const testValues = ['123', '', 'Hello world'];
for (const value of testValues) {
const testElement = mockP5Prototype.createInput(value);
@@ -921,7 +932,7 @@ suite('DOM', function() {
}
});
- test('should create input of given type and value', function() {
+ test('should create input of given type and value', function () {
const testType = 'password';
const testValue = '1234056789';
const testElement = mockP5Prototype.createInput(testValue, testType);
@@ -930,8 +941,8 @@ suite('DOM', function() {
});
});
- suite('p5.prototype.createFileInput', function() {
- afterEach(function() {
+ suite('p5.prototype.createFileInput', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -942,11 +953,11 @@ suite('DOM', function() {
});
};
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createFileInput);
});
- test('should return input of file input', function() {
+ test('should return input of file input', function () {
const testElement = mockP5Prototype.createFileInput(emptyCallback);
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLInputElement);
@@ -970,7 +981,7 @@ suite('DOM', function() {
// }
// );
- test('should accept multiple files if specified', function() {
+ test('should accept multiple files if specified', function () {
const testElement = mockP5Prototype.createFileInput(emptyCallback, true);
assert.isTrue(testElement.elt.multiple);
});
@@ -1001,8 +1012,8 @@ suite('DOM', function() {
// );
});
- suite('p5.prototype.createElement', function() {
- afterEach(function() {
+ suite('p5.prototype.createElement', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -1014,11 +1025,11 @@ suite('DOM', function() {
video: HTMLVideoElement
};
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createElement);
});
- test('should return a p5.Element of appropriate type', function() {
+ test('should return a p5.Element of appropriate type', function () {
for (const [tag, domElName] of Object.entries(testData)) {
const testElement = mockP5Prototype.createElement(tag);
assert.instanceOf(testElement, Element);
@@ -1026,19 +1037,19 @@ suite('DOM', function() {
}
});
- test('should set given content as innerHTML', function() {
+ test('should set given content as innerHTML', function () {
const testContent = 'Lorem ipsum';
const testElement = mockP5Prototype.createElement('div', testContent);
assert.deepEqual(testElement.elt.innerHTML, testContent);
});
});
- suite('p5.prototype.removeElements', function() {
- afterEach(function() {
+ suite('p5.prototype.removeElements', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should remove all elements created by p5 except Canvas', function() {
+ test('should remove all elements created by p5 except Canvas', function () {
// creates 6 elements one of which is a canvas, then calls
// removeElements and tests if only canvas is left.
const tags = ['a', 'button', 'canvas', 'div', 'p', 'video'];
@@ -1057,25 +1068,25 @@ suite('DOM', function() {
});
// p5.Element.prototype.addClass
- suite('p5.Element.prototype.addClass', function() {
- afterEach(function() {
+ suite('p5.Element.prototype.addClass', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
// Create any p5.Element
const testElement = mockP5Prototype.createElement('div');
assert.isFunction(testElement.addClass);
});
- test('should add provided string to class names', function() {
+ test('should add provided string to class names', function () {
const testClassName = 'jumbotron';
const testElement = mockP5Prototype.createElement('div');
testElement.addClass(testClassName);
assert.deepEqual(testElement.elt.className, testClassName);
});
- test('should not add class name, if already exists', function() {
+ test('should not add class name, if already exists', function () {
const testClassName1 = 'jumbotron';
const testClassName2 = 'container-fluid';
const expectedClassName = testClassName1 + ' ' + testClassName2;
@@ -1091,18 +1102,18 @@ suite('DOM', function() {
});
// p5.Element.prototype.removeClass
- suite('p5.Element.prototype.removeClass', function() {
- afterEach(function() {
+ suite('p5.Element.prototype.removeClass', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
// Create any p5.Element
const testElement = mockP5Prototype.createElement('div');
assert.isFunction(testElement.removeClass);
});
- test('should remove provided string from class names', function() {
+ test('should remove provided string from class names', function () {
const defaultClassNames = 'col-md-9 col-sm-12';
const testClassName = 'jumbotron';
@@ -1115,7 +1126,7 @@ suite('DOM', function() {
assert.deepEqual(testElement.elt.className, defaultClassNames);
});
- test('should not throw error if class name not exists', function() {
+ test('should not throw error if class name not exists', function () {
const testClassName1 = 'jumbotron';
const testClassName2 = 'container-fluid';
@@ -1130,18 +1141,18 @@ suite('DOM', function() {
});
// p5.Element.prototype.hasClass
- suite('p5.Element.prototype.hasClass', function() {
- afterEach(function() {
+ suite('p5.Element.prototype.hasClass', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
// Create any p5.Element
const testElement = mockP5Prototype.createElement('div');
assert.isFunction(testElement.hasClass);
});
- test('should return true for existing class name', function() {
+ test('should return true for existing class name', function () {
const defaultClassNames = 'col-md-9 jumbotron';
const testClassName = 'jumbotron';
@@ -1151,7 +1162,7 @@ suite('DOM', function() {
assert.isTrue(testElement.hasClass(testClassName));
});
- test('should return false for non-existing class name', function() {
+ test('should return false for non-existing class name', function () {
const defaultClassNames = 'col-md-9 jumbotron';
const testClassName = 'container-fluid';
@@ -1163,18 +1174,18 @@ suite('DOM', function() {
});
// p5.Element.prototype.toggleClass
- suite('p5.Element.prototype.toggleClass', function() {
- afterEach(function() {
+ suite('p5.Element.prototype.toggleClass', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
// Create any p5.Element
const testElement = mockP5Prototype.createElement('div');
assert.isFunction(testElement.toggleClass);
});
- test('should remove an existing class name', function() {
+ test('should remove an existing class name', function () {
const defaultClassName = 'container-fluid';
const testClassName = 'jumbotron';
@@ -1186,7 +1197,7 @@ suite('DOM', function() {
assert.deepEqual(testElement.elt.className, defaultClassName);
});
- test('should add an non-existing class name', function() {
+ test('should add an non-existing class name', function () {
const defaultClassName = 'container-fluid';
const testClassName = 'jumbotron';
@@ -1202,17 +1213,17 @@ suite('DOM', function() {
});
// p5.Element.prototype.child
- suite('p5.Element.prototype.child', function() {
- afterEach(function() {
+ suite('p5.Element.prototype.child', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
const testElement = mockP5Prototype.createElement('div');
assert.isFunction(testElement.child);
});
- test('should return all child nodes by default', function() {
+ test('should return all child nodes by default', function () {
const testElement = mockP5Prototype.createElement('div');
const childElement = mockP5Prototype.createElement('p');
@@ -1227,7 +1238,7 @@ suite('DOM', function() {
});
});
- test('should append p5 element as child', function() {
+ test('should append p5 element as child', function () {
const testElement = mockP5Prototype.createElement('div');
const childElement = mockP5Prototype.createElement('p');
@@ -1236,7 +1247,7 @@ suite('DOM', function() {
assert.isTrue(childNodes.includes(childElement.elt));
});
- test('should append dom element as child', function() {
+ test('should append dom element as child', function () {
const testElement = mockP5Prototype.createElement('div');
const childElement = mockP5Prototype.createElement('p');
@@ -1245,7 +1256,7 @@ suite('DOM', function() {
assert.isTrue(childNodes.includes(childElement.elt));
});
- test('should append element as child from a given id', function() {
+ test('should append element as child from a given id', function () {
const testElement = mockP5Prototype.createElement('div');
const childId = 'testChildElement';
const childElement = mockP5Prototype.createElement('p');
@@ -1256,7 +1267,7 @@ suite('DOM', function() {
assert.isTrue(childNodes.includes(childElement.elt));
});
- test('should not throw error if mathcing element is not found from a given id', function() {
+ test('should not throw error if mathcing element is not found from a given id', function () {
const testElement = mockP5Prototype.createElement('div');
const randomChildId = 'testChildElement';
expect(() => testElement.child(randomChildId)).to.not.throw();
@@ -1264,12 +1275,12 @@ suite('DOM', function() {
});
// p5.Element.prototype.center
- suite('p5.Element.prototype.center', function() {
- afterEach(function() {
+ suite('p5.Element.prototype.center', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
const testElement = mockP5Prototype.createElement('div');
assert.isFunction(testElement.center);
});
@@ -1288,18 +1299,18 @@ suite('DOM', function() {
});
// p5.Element.prototype.html
- suite('p5.Element.prototype.html', function() {
- afterEach(function() {
+ suite('p5.Element.prototype.html', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
// Create any p5.Element
const testElement = mockP5Prototype.createElement('a');
assert.isFunction(testElement.position);
});
- test('should return the inner HTML of element if no argument is given', function() {
+ test('should return the inner HTML of element if no argument is given', function () {
const testElement = mockP5Prototype.createElement('div');
const testHTML = 'Hello World
';
@@ -1307,7 +1318,7 @@ suite('DOM', function() {
assert.deepEqual(testElement.html(), testHTML);
});
- test('should replace the inner HTML of element', function() {
+ test('should replace the inner HTML of element', function () {
const testElement = mockP5Prototype.createElement('div');
const initialtestHTML = 'Hello World
';
const modifiedtestHTML = 'Hello World !!!
';
@@ -1319,7 +1330,7 @@ suite('DOM', function() {
assert.deepEqual(testElement.elt.innerHTML, modifiedtestHTML);
});
- test('should append to the inner HTML if second param is true', function() {
+ test('should append to the inner HTML if second param is true', function () {
const testElement = mockP5Prototype.createElement('div');
const testHTML1 = 'Hello World
';
const testHTML2 = 'Hello World !!!
';
@@ -1331,7 +1342,7 @@ suite('DOM', function() {
assert.deepEqual(testElement.elt.innerHTML, testHTML1 + testHTML2);
});
- test('should replace the inner HTML if second param is false', function() {
+ test('should replace the inner HTML if second param is false', function () {
const testElement = mockP5Prototype.createElement('div');
const testHTML1 = 'Hello World
';
const testHTML2 = 'Hello World !!!
';
@@ -1345,31 +1356,31 @@ suite('DOM', function() {
});
// p5.Element.prototype.position
- suite('p5.Element.prototype.position', function() {
- afterEach(function() {
+ suite('p5.Element.prototype.position', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
// Create any p5.Element
const testElement = mockP5Prototype.createElement('a');
assert.isFunction(testElement.position);
});
- test('should return current position if no args are given', function() {
+ test('should return current position if no args are given', function () {
const testElement = mockP5Prototype.createButton('testButton');
const position = testElement.position();
assert.deepEqual(position.x, testElement.elt.offsetLeft);
assert.deepEqual(position.y, testElement.elt.offsetTop);
});
- test('should set default position as absolute', function() {
+ test('should set default position as absolute', function () {
const testElement = mockP5Prototype.createButton('testButton');
testElement.position(20, 70);
assert.deepEqual(testElement.elt.style.position, 'absolute');
});
- test('should set given params as properties', function() {
+ test('should set given params as properties', function () {
const testElement = mockP5Prototype.createButton('testButton');
testElement.position(20, 80, 'static');
diff --git a/test/unit/dom/p5.Element.js b/test/unit/dom/p5.Element.js
index 5d744a7511..0bc859a591 100644
--- a/test/unit/dom/p5.Element.js
+++ b/test/unit/dom/p5.Element.js
@@ -2,7 +2,7 @@
import { mockP5, mockP5Prototype } from '../../js/mocks';
import dom from '../../../src/dom/dom';
-suite('p5.Element', function() {
+suite('p5.Element', function () {
// const mockP5Prototype = new p5(function(sketch) {
// sketch.setup = function() {};
// sketch.draw = function() {};
@@ -22,7 +22,7 @@ suite('p5.Element', function() {
dom(mockP5, mockP5Prototype);
});
- suite('p5.Element.prototype.parent', function() {
+ suite('p5.Element.prototype.parent', function () {
let div0, div1;
beforeEach(() => {
@@ -35,25 +35,25 @@ suite('p5.Element', function() {
div1.remove();
});
- test('attaches child to parent', function() {
+ test('attaches child to parent', function () {
div1.attribute('id', 'child');
div1.parent(div0); //attaches div1 to div0
assert.equal(document.getElementById('child').parentElement, div0.elt);
});
- test('attaches child to parent using classname', function() {
+ test('attaches child to parent using classname', function () {
div0.attribute('id', 'parent');
div1.parent('parent'); //attaches div1 to div0 using classname
assert.equal(div1.parent(), div0.elt); //returns parent of div1
});
- test('attaches child to parent using id', function() {
+ test('attaches child to parent using id', function () {
div0.attribute('id', 'parent');
div1.parent('#parent'); //attaches div1 to div0
assert.equal(div1.parent(), div0.elt); //returns parent of div1 using id
});
- test('returns the parent', function() {
+ test('returns the parent', function () {
let div0 = document.createElement('div');
let div1 = document.createElement('div');
div1.setAttribute('id', 'child');
@@ -63,14 +63,14 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.id', function() {
- test('attaches child to parent', function() {
+ suite('p5.Element.prototype.id', function () {
+ test('attaches child to parent', function () {
const elt = mockP5Prototype.createDiv();
elt.id('test');
assert.equal(document.getElementById('test'), elt.elt);
});
- test('returns the id', function() {
+ test('returns the id', function () {
const elt = document.createElement('div');
elt.setAttribute('id', 'test');
document.body.appendChild(elt);
@@ -78,12 +78,12 @@ suite('p5.Element', function() {
});
});
- suite.todo('p5.Element.prototype.mousePressed', function() {
- test('attaches and gets events', function() {
+ suite.todo('p5.Element.prototype.mousePressed', function () {
+ test('attaches and gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -93,15 +93,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -114,12 +114,12 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.mouseClicked', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.mouseClicked', function () {
+ test('attaches and gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -129,15 +129,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -149,11 +149,11 @@ suite('p5.Element', function() {
assert.equal(myFnCounterOther, 1);
});
- test('detaches and does not get events', function() {
+ test('detaches and does not get events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -165,12 +165,12 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.doubleClicked', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.doubleClicked', function () {
+ test('attaches and gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -180,15 +180,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -200,11 +200,11 @@ suite('p5.Element', function() {
assert.equal(myFnCounterOther, 1);
});
- test('detaches and does not get events', function() {
+ test('detaches and does not get events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -216,12 +216,12 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.mouseWheel', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.mouseWheel', function () {
+ test('attaches and gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function(event) {
+ var myFn = function (event) {
if (event.deltaX > 0) {
myFnCounter++;
}
@@ -233,15 +233,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -254,12 +254,12 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.mouseReleased', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.mouseReleased', function () {
+ test('attaches and gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -269,15 +269,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -289,11 +289,11 @@ suite('p5.Element', function() {
assert.equal(myFnCounterOther, 1);
});
- test('detaches and does not get events', function() {
+ test('detaches and does not get events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -305,12 +305,12 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.mouseMoved', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.mouseMoved', function () {
+ test('attaches and gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -320,15 +320,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -340,11 +340,11 @@ suite('p5.Element', function() {
assert.equal(myFnCounterOther, 1);
});
- test('detaches and does not get events', function() {
+ test('detaches and does not get events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -356,12 +356,12 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.mouseOver', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.mouseOver', function () {
+ test('attaches and gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -371,15 +371,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -391,11 +391,11 @@ suite('p5.Element', function() {
assert.equal(myFnCounterOther, 1);
});
- test('detaches and does not get events', function() {
+ test('detaches and does not get events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -407,12 +407,12 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.mouseOut', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.mouseOut', function () {
+ test('attaches and gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -422,15 +422,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -442,11 +442,11 @@ suite('p5.Element', function() {
assert.equal(myFnCounterOther, 1);
});
- test('detaches and does not get events', function() {
+ test('detaches and does not get events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -458,11 +458,11 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.dragOver', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.dragOver', function () {
+ test('attaches and gets events', function () {
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -472,15 +472,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -492,11 +492,11 @@ suite('p5.Element', function() {
assert.equal(myFnCounterOther, 1);
});
- test('detaches and does not get events', function() {
+ test('detaches and does not get events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -508,11 +508,11 @@ suite('p5.Element', function() {
});
});
- suite('p5.Element.prototype.dragLeave', function() {
- test('attaches and gets events', function() {
+ suite('p5.Element.prototype.dragLeave', function () {
+ test('attaches and gets events', function () {
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -522,15 +522,15 @@ suite('p5.Element', function() {
assert.equal(myFnCounter, 1);
});
- test('attaches multiple handlers and only latest gets events', function() {
+ test('attaches multiple handlers and only latest gets events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
var myFnCounterOther = 0;
- var myFnOther = function() {
+ var myFnOther = function () {
myFnCounterOther++;
};
@@ -542,11 +542,11 @@ suite('p5.Element', function() {
assert.equal(myFnCounterOther, 1);
});
- test('detaches and does not get events', function() {
+ test('detaches and does not get events', function () {
// setup
const elt = mockP5Prototype.createDiv('hello');
var myFnCounter = 0;
- var myFn = function() {
+ var myFn = function () {
myFnCounter++;
};
@@ -558,7 +558,7 @@ suite('p5.Element', function() {
});
});
- suite('operating with element classes', function() {
+ suite('operating with element classes', function () {
let elt;
beforeEach(() => {
@@ -569,7 +569,7 @@ suite('p5.Element', function() {
elt.remove();
});
- test('should add class to element', function() {
+ test('should add class to element', function () {
elt.setAttribute('id', 'testdiv');
document.body.appendChild(elt);
@@ -577,7 +577,7 @@ suite('p5.Element', function() {
assert.strictEqual(elt.getAttribute('class'), 'testclass');
});
- test('should remove class from element with only one class', function() {
+ test('should remove class from element with only one class', function () {
elt.setAttribute('id', 'testdiv');
elt.setAttribute('class', 'testclass');
document.body.appendChild(elt);
@@ -586,7 +586,7 @@ suite('p5.Element', function() {
assert.strictEqual(elt.getAttribute('class'), '');
});
- test('should remove class from element with several classes', function() {
+ test('should remove class from element with several classes', function () {
elt.setAttribute('id', 'testdiv');
elt.setAttribute('class', 'testclass1 testclass2 testclass3');
document.body.appendChild(elt);
@@ -595,34 +595,52 @@ suite('p5.Element', function() {
assert.strictEqual(elt.getAttribute('class'), 'testclass1 testclass3');
});
- test('should return true if element has specified class', function() {
+ test('should return true if element has specified class', function () {
elt.setAttribute('id', 'testdiv');
elt.setAttribute('class', 'testclass1 testclass2 testclass3');
document.body.appendChild(elt);
- assert.strictEqual(mockP5Prototype.select('#testdiv').hasClass('testclass2'), true);
+ assert.strictEqual(
+ mockP5Prototype.select('#testdiv').hasClass('testclass2'),
+ true
+ );
});
- test('should return false if element has not specified class', function() {
+ test('should return false if element has not specified class', function () {
elt.setAttribute('id', 'testdiv');
elt.setAttribute('class', 'testclass1 testclass3');
document.body.appendChild(elt);
- assert.strictEqual(mockP5Prototype.select('#testdiv').hasClass('testclass2'), false);
+ assert.strictEqual(
+ mockP5Prototype.select('#testdiv').hasClass('testclass2'),
+ false
+ );
});
- test('should return false if element has class that is partially similar as specified class', function() {
+ test('should return false if element has class that is partially similar as specified class', function () {
elt.setAttribute('id', 'testdiv');
elt.setAttribute('class', 'testclass slideshow newtestsclas');
document.body.appendChild(elt);
- assert.strictEqual(mockP5Prototype.select('#testdiv').hasClass('show'), false);
- assert.strictEqual(mockP5Prototype.select('#testdiv').hasClass('slide'), false);
- assert.strictEqual(mockP5Prototype.select('#testdiv').hasClass('test'), false);
- assert.strictEqual(mockP5Prototype.select('#testdiv').hasClass('class'), false);
- });
-
- test('should toggle specified class on element', function() {
+ assert.strictEqual(
+ mockP5Prototype.select('#testdiv').hasClass('show'),
+ false
+ );
+ assert.strictEqual(
+ mockP5Prototype.select('#testdiv').hasClass('slide'),
+ false
+ );
+ assert.strictEqual(
+ mockP5Prototype.select('#testdiv').hasClass('test'),
+ false
+ );
+ assert.strictEqual(
+ mockP5Prototype.select('#testdiv').hasClass('class'),
+ false
+ );
+ });
+
+ test('should toggle specified class on element', function () {
elt.setAttribute('id', 'testdiv');
elt.setAttribute('class', 'testclass1 testclass2');
document.body.appendChild(elt);
diff --git a/test/unit/dom/p5.MediaElement.js b/test/unit/dom/p5.MediaElement.js
index 38718c9821..6e10b59d56 100644
--- a/test/unit/dom/p5.MediaElement.js
+++ b/test/unit/dom/p5.MediaElement.js
@@ -1,6 +1,9 @@
import { vi } from 'vitest';
import { mockP5, mockP5Prototype } from '../../js/mocks';
-import { default as media, MediaElement } from '../../../src/dom/p5.MediaElement';
+import {
+ default as media,
+ MediaElement
+} from '../../../src/dom/p5.MediaElement';
import { Element } from '../../../src/dom/p5.Element';
import { default as pixels } from '../../../src/image/pixels';
@@ -8,7 +11,8 @@ suite('p5.MediaElement', () => {
beforeAll(() => {
media(mockP5, mockP5Prototype);
pixels(mockP5, mockP5Prototype);
- navigator.mediaDevices.getUserMedia = vi.fn()
+ navigator.mediaDevices.getUserMedia = vi
+ .fn()
.mockResolvedValue('stream-value');
});
@@ -16,8 +20,8 @@ suite('p5.MediaElement', () => {
vi.restoreAllMocks();
});
- suite('p5.prototype.createVideo', function() {
- afterEach(function() {
+ suite('p5.prototype.createVideo', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -26,17 +30,17 @@ suite('p5.MediaElement', () => {
'/test/unit/assets/target.gif'
];
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createVideo);
});
- test('should return p5.Element of HTMLVideoElement', function() {
+ test('should return p5.Element of HTMLVideoElement', function () {
const testElement = mockP5Prototype.createVideo('');
assert.instanceOf(testElement, MediaElement);
assert.instanceOf(testElement.elt, HTMLVideoElement);
});
- test('should accept a singular media source', function() {
+ test('should accept a singular media source', function () {
const mediaSource = mediaSources[0];
const testElement = mockP5Prototype.createVideo(mediaSource);
const sourceEl = testElement.elt.children[0];
@@ -46,7 +50,7 @@ suite('p5.MediaElement', () => {
assert.isTrue(sourceEl.src.endsWith(mediaSource));
});
- test('should accept multiple media sources', function() {
+ test('should accept multiple media sources', function () {
const testElement = mockP5Prototype.createVideo(mediaSources);
assert.deepEqual(testElement.elt.childElementCount, mediaSources.length);
@@ -68,7 +72,7 @@ suite('p5.MediaElement', () => {
// );
// TODO: integration test
- test.todo('should work with tint()', function(done) {
+ test.todo('should work with tint()', function (done) {
const imgElt = myp5.createImg('/test/unit/assets/cat.jpg', '');
const testElement = myp5.createVideo('/test/unit/assets/cat.webm', () => {
// Workaround for headless tests, where the video data isn't loading
@@ -92,7 +96,7 @@ suite('p5.MediaElement', () => {
});
});
- test.todo('should work with updatePixels()', function(done) {
+ test.todo('should work with updatePixels()', function (done) {
let loaded = false;
let prevElt;
const imgElt = myp5.createImg('/test/unit/assets/cat.jpg', '');
@@ -105,7 +109,7 @@ suite('p5.MediaElement', () => {
});
let drewUpdatedPixels = false;
- myp5.draw = function() {
+ myp5.draw = function () {
if (!loaded) return;
myp5.background(255);
@@ -137,8 +141,9 @@ suite('p5.MediaElement', () => {
myp5.loadPixels();
testElement.loadPixels();
- expect([...testElement.pixels.slice(0, 4)])
- .to.not.deep.equal([255, 0, 0, 255]);
+ expect([...testElement.pixels.slice(0, 4)]).to.not.deep.equal([
+ 255, 0, 0, 255
+ ]);
assert.deepEqual(
[...myp5.pixels.slice(0, 4)],
[...testElement.pixels.slice(0, 4)]
@@ -151,8 +156,8 @@ suite('p5.MediaElement', () => {
});
});
- suite('p5.prototype.createAudio', function() {
- afterEach(function() {
+ suite('p5.prototype.createAudio', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
@@ -161,17 +166,17 @@ suite('p5.MediaElement', () => {
'/test/unit/assets/beat.mp3'
];
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createAudio);
});
- test('should return p5.Element of HTMLAudioElement', function() {
+ test('should return p5.Element of HTMLAudioElement', function () {
const testElement = mockP5Prototype.createAudio('');
assert.instanceOf(testElement, MediaElement);
assert.instanceOf(testElement.elt, HTMLAudioElement);
});
- test('should accept a singular media source', function() {
+ test('should accept a singular media source', function () {
const mediaSource = mediaSources[0];
const testElement = mockP5Prototype.createAudio(mediaSource);
const sourceEl = testElement.elt.children[0];
@@ -181,7 +186,7 @@ suite('p5.MediaElement', () => {
assert.isTrue(sourceEl.src.endsWith(mediaSource));
});
- test('should accept multiple media sources', function() {
+ test('should accept multiple media sources', function () {
const testElement = mockP5Prototype.createAudio(mediaSources);
assert.deepEqual(testElement.elt.childElementCount, mediaSources.length);
@@ -203,16 +208,16 @@ suite('p5.MediaElement', () => {
// );
});
- suite('p5.prototype.createCapture', function() {
- afterEach(function() {
+ suite('p5.prototype.createCapture', function () {
+ afterEach(function () {
document.body.innerHTML = '';
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.isFunction(mockP5Prototype.createCapture);
});
- test('should return p5.Element of video type', function() {
+ test('should return p5.Element of video type', function () {
const testElement = mockP5Prototype.createCapture(mockP5Prototype.VIDEO);
assert.instanceOf(testElement, Element);
assert.instanceOf(testElement.elt, HTMLVideoElement);
@@ -231,7 +236,7 @@ suite('p5.MediaElement', () => {
// );
// Required for ios 11 devices
- test('should have playsinline attribute to empty string on DOM element', function() {
+ test('should have playsinline attribute to empty string on DOM element', function () {
const testElement = mockP5Prototype.createCapture(mockP5Prototype.VIDEO);
// Weird check, setter accepts : playinline, getter accepts playInline
assert.isTrue(testElement.elt.playsInline);
@@ -248,8 +253,10 @@ suite('p5.MediaElement', () => {
document.body.innerHTML = '';
});
- test('should not throw an error', function() {
- const testElement = mockP5Prototype.createVideo('/test/unit/assets/nyan_cat.gif');
+ test('should not throw an error', function () {
+ const testElement = mockP5Prototype.createVideo(
+ '/test/unit/assets/nyan_cat.gif'
+ );
assert.doesNotThrow(() => {
testElement.copy(0, 0, 10, 10, 0, 0, 10, 10);
});
diff --git a/test/unit/events/acceleration.js b/test/unit/events/acceleration.js
index 6918ba1d1a..7ab51d1529 100644
--- a/test/unit/events/acceleration.js
+++ b/test/unit/events/acceleration.js
@@ -1,17 +1,17 @@
import p5 from '../../../src/app.js';
-suite('Acceleration Events', function() {
+suite('Acceleration Events', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
@@ -41,76 +41,76 @@ suite('Acceleration Events', function() {
}
);
- suite('acceleration', function() {
- test('accelerationX should be 20', function() {
+ suite('acceleration', function () {
+ test('accelerationX should be 20', function () {
window.dispatchEvent(deviceMotionEvent1);
assert.strictEqual(myp5.accelerationX, 20);
});
- test('accelerationY should be 40', function() {
+ test('accelerationY should be 40', function () {
window.dispatchEvent(deviceMotionEvent1);
assert.strictEqual(myp5.accelerationY, 40);
});
- test('accelerationZ should be 60', function() {
+ test('accelerationZ should be 60', function () {
window.dispatchEvent(deviceMotionEvent1);
assert.strictEqual(myp5.accelerationZ, 60);
});
});
- suite('previous acceleration', function() {
- test('pAccelerationX should be 20', function() {
+ suite('previous acceleration', function () {
+ test('pAccelerationX should be 20', function () {
window.dispatchEvent(deviceMotionEvent1);
window.dispatchEvent(deviceMotionEvent2);
assert.strictEqual(myp5.pAccelerationX, 20);
});
- test('pAccelerationY should be 40', function() {
+ test('pAccelerationY should be 40', function () {
window.dispatchEvent(deviceMotionEvent1);
window.dispatchEvent(deviceMotionEvent2);
assert.strictEqual(myp5.pAccelerationY, 40);
});
- test('pAccelerationZ should be 60', function() {
+ test('pAccelerationZ should be 60', function () {
window.dispatchEvent(deviceMotionEvent1);
window.dispatchEvent(deviceMotionEvent2);
assert.strictEqual(myp5.pAccelerationZ, 60);
});
});
- suite('rotation', function() {
- test('rotationX should be 45', function() {
+ suite('rotation', function () {
+ test('rotationX should be 45', function () {
window.dispatchEvent(deviceOrientationEvent1);
assert.strictEqual(myp5.rotationX, 45 * (Math.PI / 180.0));
});
- test('rotationY should be 90', function() {
+ test('rotationY should be 90', function () {
window.dispatchEvent(deviceOrientationEvent1);
assert.strictEqual(myp5.rotationY, 90 * (Math.PI / 180.0));
});
- test('rotationZ should be 10', function() {
+ test('rotationZ should be 10', function () {
window.dispatchEvent(deviceOrientationEvent1);
assert.strictEqual(myp5.rotationZ, 10 * (Math.PI / 180.0));
});
});
- suite('previous rotation', function() {
- test('pRotationX should be 45', function() {
+ suite('previous rotation', function () {
+ test('pRotationX should be 45', function () {
window.dispatchEvent(deviceOrientationEvent1);
window.dispatchEvent(deviceOrientationEvent2);
assert.strictEqual(myp5.pRotationX, 45 * (Math.PI / 180.0));
});
- test('pRotationY should be 90', function() {
+ test('pRotationY should be 90', function () {
window.dispatchEvent(deviceOrientationEvent1);
window.dispatchEvent(deviceOrientationEvent2);
assert.strictEqual(myp5.pRotationY, 90 * (Math.PI / 180.0));
});
- test('pRotationZ should be 10', function() {
+ test('pRotationZ should be 10', function () {
window.dispatchEvent(deviceOrientationEvent1);
window.dispatchEvent(deviceOrientationEvent2);
assert.strictEqual(myp5.pRotationZ, 10 * (Math.PI / 180.0));
});
});
- suite.todo('deviceMoved', function() {
- test('deviceMoved must run when device is moved more than the threshold value', function() {
+ suite.todo('deviceMoved', function () {
+ test('deviceMoved must run when device is moved more than the threshold value', function () {
let count = 0;
- myp5.deviceMoved = function() {
+ myp5.deviceMoved = function () {
count += 1;
};
window.dispatchEvent(
@@ -119,9 +119,9 @@ suite('Acceleration Events', function() {
assert.strictEqual(count, 1);
});
- test('deviceMoved should not run when device is moved less than the threshold value', function() {
+ test('deviceMoved should not run when device is moved less than the threshold value', function () {
let count = 0;
- myp5.deviceMoved = function() {
+ myp5.deviceMoved = function () {
count += 1;
};
window.dispatchEvent(
@@ -131,9 +131,9 @@ suite('Acceleration Events', function() {
assert.strictEqual(count, 0);
});
- test('p5.prototype.setMoveThreshold', function() {
+ test('p5.prototype.setMoveThreshold', function () {
let count = 0;
- myp5.deviceMoved = function() {
+ myp5.deviceMoved = function () {
count += 1;
};
myp5.setMoveThreshold(0.1);
@@ -145,10 +145,10 @@ suite('Acceleration Events', function() {
});
});
- suite.todo('deviceTurned', function() {
- test('deviceTurned must run when device is turned more than 90 degrees', function() {
+ suite.todo('deviceTurned', function () {
+ test('deviceTurned must run when device is turned more than 90 degrees', function () {
let count = 0;
- myp5.deviceTurned = function() {
+ myp5.deviceTurned = function () {
count += 1;
};
window.dispatchEvent(
@@ -160,9 +160,9 @@ suite('Acceleration Events', function() {
assert.strictEqual(count, 1);
});
- test('turnAxis should be X', function() {
+ test('turnAxis should be X', function () {
let count = 0;
- myp5.deviceTurned = function() {
+ myp5.deviceTurned = function () {
if (myp5.turnAxis === 'X') count += 1;
};
window.dispatchEvent(
@@ -175,10 +175,10 @@ suite('Acceleration Events', function() {
});
});
- suite.todo('deviceShaken', function() {
- test('deviceShaken must run when device acceleration is more than the threshold value', function() {
+ suite.todo('deviceShaken', function () {
+ test('deviceShaken must run when device acceleration is more than the threshold value', function () {
let count = 0;
- myp5.deviceShaken = function() {
+ myp5.deviceShaken = function () {
count += 1;
};
window.dispatchEvent(
@@ -187,9 +187,9 @@ suite('Acceleration Events', function() {
assert.strictEqual(count, 1);
});
- test('deviceMoved should not run when device acceleration is less than the threshold value', function() {
+ test('deviceMoved should not run when device acceleration is less than the threshold value', function () {
let count = 0;
- myp5.deviceShaken = function() {
+ myp5.deviceShaken = function () {
count += 1;
};
window.dispatchEvent(
@@ -199,9 +199,9 @@ suite('Acceleration Events', function() {
assert.strictEqual(count, 0);
});
- test('p5.prototype.setShakeThreshold', function() {
+ test('p5.prototype.setShakeThreshold', function () {
let count = 0;
- myp5.deviceShaken = function() {
+ myp5.deviceShaken = function () {
count += 1;
};
myp5.setShakeThreshold(10);
diff --git a/test/unit/events/keyboard.js b/test/unit/events/keyboard.js
index e8280dc87d..db5431313c 100644
--- a/test/unit/events/keyboard.js
+++ b/test/unit/events/keyboard.js
@@ -2,81 +2,81 @@ import p5 from '../../../src/app.js';
import { isCode } from '../../../src/events/keyboard.js';
import { parallelSketches } from '../../js/p5_helpers';
-suite('Keyboard Events', function() {
+suite('Keyboard Events', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.prototype.keyIsPressed', function() {
- test('keyIsPressed should be a boolean', function() {
+ suite('p5.prototype.keyIsPressed', function () {
+ test('keyIsPressed should be a boolean', function () {
assert.isBoolean(myp5.keyIsPressed);
});
- test('keyIsPressed should be true on key press', function() {
+ test('keyIsPressed should be true on key press', function () {
window.dispatchEvent(new KeyboardEvent('keydown'));
assert.strictEqual(myp5.keyIsPressed, true);
});
- test('keyIsPressed should be true on multiple key presses', function() {
+ test('keyIsPressed should be true on multiple key presses', function () {
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Down' }));
assert.strictEqual(myp5.keyIsPressed, true);
});
- test('keyIsPressed should be false on key up', function() {
+ test('keyIsPressed should be false on key up', function () {
window.dispatchEvent(new KeyboardEvent('keyup'));
assert.strictEqual(myp5.keyIsPressed, false);
});
});
- suite('p5.prototype.key', function() {
- test('key should be a string', async function() {
+ suite('p5.prototype.key', function () {
+ test('key should be a string', async function () {
window.dispatchEvent(new KeyboardEvent('keydown', { key: 's' }));
assert.isString(myp5.key);
});
- test.todo('key should return the key pressed', function() {
+ test.todo('key should return the key pressed', function () {
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'A' }));
assert.strictEqual(myp5.key, 'A');
});
- test.todo('key should return the key pressed', function() {
+ test.todo('key should return the key pressed', function () {
window.dispatchEvent(new KeyboardEvent('keydown', { key: '9' }));
assert.strictEqual(myp5.key, '9');
});
- test.todo('key should return the key pressed', function() {
+ test.todo('key should return the key pressed', function () {
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'CapsLock' }));
assert.strictEqual(myp5.key, 'CapsLock');
});
});
- suite('p5.prototype.keyCode', function() {
- test('keyCode should be a number', function() {
+ suite('p5.prototype.keyCode', function () {
+ test('keyCode should be a number', function () {
window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 65 }));
assert.isNumber(myp5.keyCode);
});
- test('key should return the key pressed', function() {
+ test('key should return the key pressed', function () {
window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 65 }));
assert.strictEqual(myp5.keyCode, 65);
});
});
- suite('keyPressed', function() {
- test.todo('keyPressed must run when key is pressed', function() {
+ suite('keyPressed', function () {
+ test.todo('keyPressed must run when key is pressed', function () {
let count = 0;
- myp5.keyPressed = function() {
+ myp5.keyPressed = function () {
count += 1;
};
window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 65 }));
@@ -84,14 +84,14 @@ suite('Keyboard Events', function() {
assert.strictEqual(count, 2);
});
- test('keyPressed functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('keyPressed functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.keyPressed = function() {
+ sketch.keyPressed = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -104,25 +104,25 @@ suite('Keyboard Events', function() {
});
});
- suite('keyReleased', function() {
- test('keyReleased must run when key is released', function() {
+ suite('keyReleased', function () {
+ test('keyReleased must run when key is released', function () {
let count = 0;
- myp5.keyReleased = function() {
+ myp5.keyReleased = function () {
count += 1;
};
window.dispatchEvent(new KeyboardEvent('keyup'));
assert.strictEqual(count, 1);
});
- test('keyReleased functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('keyReleased functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.keyReleased = function() {
+ sketch.keyReleased = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -135,24 +135,24 @@ suite('Keyboard Events', function() {
});
});
- suite('keyTyped', function() {
- test('keyTyped must run when key is pressed', function() {
+ suite('keyTyped', function () {
+ test('keyTyped must run when key is pressed', function () {
let count = 0;
- myp5.keyTyped = function() {
+ myp5.keyTyped = function () {
count += 1;
};
window.dispatchEvent(new KeyboardEvent('keypress'));
assert.strictEqual(count, 1);
});
- test('keyTyped functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('keyTyped functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.keyTyped = function() {
+ sketch.keyTyped = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -165,8 +165,8 @@ suite('Keyboard Events', function() {
});
});
- suite('isCode', function() {
- test('returns false for non-string inputs', function() {
+ suite('isCode', function () {
+ test('returns false for non-string inputs', function () {
assert.isFalse(isCode(null));
assert.isFalse(isCode(undefined));
assert.isFalse(isCode(123));
@@ -174,7 +174,7 @@ suite('Keyboard Events', function() {
assert.isFalse(isCode([]));
});
- test('returns false for single non-digit and digit characters', function() {
+ test('returns false for single non-digit and digit characters', function () {
assert.isFalse(isCode('a'));
assert.isFalse(isCode('Z'));
assert.isFalse(isCode('1'));
@@ -182,51 +182,54 @@ suite('Keyboard Events', function() {
assert.isFalse(isCode(' '));
});
- test('returns true for multi-character strings', function() {
+ test('returns true for multi-character strings', function () {
assert.isTrue(isCode('ShiftLeft'));
assert.isTrue(isCode('ArrowUp'));
assert.isTrue(isCode('ab'));
});
- test('returns false/true for apppropriate leftright keys and codes', function() {
+ test('returns false/true for apppropriate leftright keys and codes', function () {
assert.isFalse(isCode('Alt'));
assert.isFalse(isCode('Shift'));
assert.isTrue(isCode('AltLeft'));
assert.isTrue(isCode('ShiftLeft'));
});
- test('handles edge cases correctly', function() {
- assert.isFalse(isCode('')); // empty string
+ test('handles edge cases correctly', function () {
+ assert.isFalse(isCode('')); // empty string
assert.isTrue(isCode('11')); // multi-digit number
assert.isTrue(isCode('1a')); // digit + letter
});
});
- suite('p5.prototype.keyIsDown', function() {
- test('keyIsDown should return a boolean', function() {
+ suite('p5.prototype.keyIsDown', function () {
+ test('keyIsDown should return a boolean', function () {
assert.isBoolean(myp5.keyIsDown('a'));
assert.isBoolean(myp5.keyIsDown('Enter'));
});
- test('keyIsDown should return true if key is down', function() {
+ test('keyIsDown should return true if key is down', function () {
// Test single character keys
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' }));
assert.strictEqual(myp5.keyIsDown('a'), true);
// Test digit keys
- window.dispatchEvent(new KeyboardEvent('keydown', { key: '1', code: 'Digit1' }));
+ window.dispatchEvent(
+ new KeyboardEvent('keydown', { key: '1', code: 'Digit1' })
+ );
assert.strictEqual(myp5.keyIsDown('1'), true);
// Test special keys
- window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter' }));
+ window.dispatchEvent(
+ new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter' })
+ );
assert.strictEqual(myp5.keyIsDown('Enter'), true);
});
- test('keyIsDown should return false if key is not down', function() {
+ test('keyIsDown should return false if key is not down', function () {
// Ensure key is not down
window.dispatchEvent(new KeyboardEvent('keyup'));
assert.strictEqual(myp5.keyIsDown('z'), false);
-
});
});
});
diff --git a/test/unit/events/mouse.js b/test/unit/events/mouse.js
index 0e02ece634..392ca7c165 100644
--- a/test/unit/events/mouse.js
+++ b/test/unit/events/mouse.js
@@ -1,7 +1,7 @@
import p5 from '../../../src/app.js';
import { parallelSketches } from '../../js/p5_helpers';
-suite.todo('Mouse Events', function() {
+suite.todo('Mouse Events', function () {
let myp5;
let canvas;
@@ -13,9 +13,9 @@ suite.todo('Mouse Events', function() {
let touchEvent1;
let touchEvent2;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
canvas = myp5._curElement.elt;
rect = canvas.getBoundingClientRect();
@@ -43,82 +43,88 @@ suite.todo('Mouse Events', function() {
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- let mouseEvent1 = new PointerEvent('pointermove', { clientX: 100, clientY: 100 });
- let mouseEvent2 = new PointerEvent('pointermove', { clientX: 200, clientY: 200 });
+ let mouseEvent1 = new PointerEvent('pointermove', {
+ clientX: 100,
+ clientY: 100
+ });
+ let mouseEvent2 = new PointerEvent('pointermove', {
+ clientX: 200,
+ clientY: 200
+ });
- suite('p5.prototype._hasMouseInteracted', function() {
- test('_hasMouseInteracted should be a boolean', function() {
+ suite('p5.prototype._hasMouseInteracted', function () {
+ test('_hasMouseInteracted should be a boolean', function () {
assert.isBoolean(myp5._hasMouseInteracted);
});
- test('_hasMouseInteracted should be false before mouse interaction', function() {
+ test('_hasMouseInteracted should be false before mouse interaction', function () {
assert.strictEqual(myp5._hasMouseInteracted, false);
});
- test('_hasMouseInteracted should be true on mouse interaction', function() {
+ test('_hasMouseInteracted should be true on mouse interaction', function () {
window.dispatchEvent(mouseEvent1);
assert.strictEqual(myp5._hasMouseInteracted, true);
});
- test('_hasMouseInteracted should be true on touch interaction', function() {
+ test('_hasMouseInteracted should be true on touch interaction', function () {
window.dispatchEvent(touchEvent1);
assert.strictEqual(myp5._hasMouseInteracted, true);
});
});
- suite('p5.prototype.mouseX', function() {
- test('mouseX should be a number', function() {
+ suite('p5.prototype.mouseX', function () {
+ test('mouseX should be a number', function () {
assert.isNumber(myp5.mouseX);
});
- test('mouseX should be current horizontal position of mouse relative to the canvas', function() {
+ test('mouseX should be current horizontal position of mouse relative to the canvas', function () {
let result = (mouseEvent1.clientX - rect.left) / sx;
window.dispatchEvent(mouseEvent1);
assert.strictEqual(myp5.mouseX, result);
});
- test('mouseX should be current horizontal position of touch relative to the canvas', function() {
+ test('mouseX should be current horizontal position of touch relative to the canvas', function () {
let result = (touchObj1.clientX - rect.left) / sx;
window.dispatchEvent(touchEvent1);
assert.strictEqual(myp5.mouseX, result);
});
});
- suite('p5.prototype.mouseY', function() {
- test('mouseY should be a number', function() {
+ suite('p5.prototype.mouseY', function () {
+ test('mouseY should be a number', function () {
assert.isNumber(myp5.mouseY);
});
- test('mouseY should be current vertical position of mouse relative to the canvas', function() {
+ test('mouseY should be current vertical position of mouse relative to the canvas', function () {
let result = (mouseEvent1.clientY - rect.top) / sy;
window.dispatchEvent(mouseEvent1);
assert.strictEqual(myp5.mouseY, result);
});
- test('mouseY should be current vertical position of touch relative to the canvas', function() {
+ test('mouseY should be current vertical position of touch relative to the canvas', function () {
let result = (touchObj1.clientY - rect.top) / sy;
window.dispatchEvent(touchEvent1);
assert.strictEqual(myp5.mouseY, result);
});
});
- suite('p5.prototype.pmouseX', function() {
- test('pmouseX should be a number', function() {
+ suite('p5.prototype.pmouseX', function () {
+ test('pmouseX should be a number', function () {
assert.isNumber(myp5.pmouseX);
});
- test('pmouseX should be previous horizontal position of mouse relative to the canvas', function() {
+ test('pmouseX should be previous horizontal position of mouse relative to the canvas', function () {
window.dispatchEvent(mouseEvent1); // dispatch first mouse event
window.dispatchEvent(mouseEvent2); // dispatch second mouse event
let result = (mouseEvent1.clientX - rect.left) / sx;
assert.strictEqual(myp5.pmouseX, result);
});
- test('pmouseX should be previous horizontal position of touch relative to the canvas', function() {
+ test('pmouseX should be previous horizontal position of touch relative to the canvas', function () {
window.dispatchEvent(touchEvent1); // dispatch first touch event
window.dispatchEvent(touchEvent2); // dispatch second touch event
let result = (touchObj1.clientX - rect.left) / sx;
@@ -126,133 +132,133 @@ suite.todo('Mouse Events', function() {
});
});
- suite('p5.prototype.pmouseY', function() {
- test('pmouseY should be a number', function() {
+ suite('p5.prototype.pmouseY', function () {
+ test('pmouseY should be a number', function () {
assert.isNumber(myp5.pmouseY);
});
- test('pmouseY should be previous vertical position of mouse relative to the canvas', function() {
+ test('pmouseY should be previous vertical position of mouse relative to the canvas', function () {
window.dispatchEvent(mouseEvent1); // dispatch first mouse event
window.dispatchEvent(mouseEvent2); // dispatch second mouse event
let result = (mouseEvent1.clientY - rect.top) / sy;
assert.strictEqual(myp5.pmouseY, result);
});
- test('pmouseY should be previous vertical position of touch relative to the canvas', function() {
+ test('pmouseY should be previous vertical position of touch relative to the canvas', function () {
window.dispatchEvent(touchEvent1); // dispatch first touch event
window.dispatchEvent(touchEvent2); // dispatch second touch event
let result = (touchObj1.clientY - rect.top) / sy;
assert.strictEqual(myp5.pmouseY, result);
});
});
- suite('p5.prototype.winMouseX', function() {
- test('winMouseX should be a number', function() {
+ suite('p5.prototype.winMouseX', function () {
+ test('winMouseX should be a number', function () {
assert.isNumber(myp5.winMouseX);
});
- test('winMouseX should be current horizontal position of mouse relative to the window', function() {
+ test('winMouseX should be current horizontal position of mouse relative to the window', function () {
window.dispatchEvent(mouseEvent1);
assert.strictEqual(myp5.winMouseX, mouseEvent1.clientX);
});
- test('winMouseX should be current horizontal position of touch relative to the window', function() {
+ test('winMouseX should be current horizontal position of touch relative to the window', function () {
window.dispatchEvent(touchEvent1);
assert.strictEqual(myp5.winMouseX, touchObj1.clientX);
});
});
- suite('p5.prototype.winMouseY', function() {
- test('winMouseY should be a number', function() {
+ suite('p5.prototype.winMouseY', function () {
+ test('winMouseY should be a number', function () {
assert.isNumber(myp5.winMouseY);
});
- test('winMouseY should be current vertical position of mouse relative to the window', function() {
+ test('winMouseY should be current vertical position of mouse relative to the window', function () {
window.dispatchEvent(mouseEvent1);
assert.strictEqual(myp5.winMouseY, mouseEvent1.clientY);
});
- test('winMouseY should be current vertical position of touch relative to the window', function() {
+ test('winMouseY should be current vertical position of touch relative to the window', function () {
window.dispatchEvent(touchEvent1);
assert.strictEqual(myp5.winMouseY, touchObj1.clientY);
});
});
- suite('p5.prototype.pwinMouseX', function() {
- test('pwinMouseX should be a number', function() {
+ suite('p5.prototype.pwinMouseX', function () {
+ test('pwinMouseX should be a number', function () {
assert.isNumber(myp5.pwinMouseX);
});
- test('pwinMouseX should be previous horizontal position of mouse relative to the window', function() {
+ test('pwinMouseX should be previous horizontal position of mouse relative to the window', function () {
window.dispatchEvent(mouseEvent1); // dispatch first mouse event
window.dispatchEvent(mouseEvent2); // dispatch second mouse event
assert.strictEqual(myp5.pwinMouseX, mouseEvent1.clientX);
});
- test('pwinMouseX should be previous horizontal position of touch relative to the window', function() {
+ test('pwinMouseX should be previous horizontal position of touch relative to the window', function () {
window.dispatchEvent(touchEvent1); // dispatch first touch event
window.dispatchEvent(touchEvent2); // dispatch second touch event
assert.strictEqual(myp5.pwinMouseX, touchObj1.clientX);
});
});
- suite('p5.prototype.pwinMouseY', function() {
- test('pwinMouseY should be a number', function() {
+ suite('p5.prototype.pwinMouseY', function () {
+ test('pwinMouseY should be a number', function () {
assert.isNumber(myp5.pwinMouseY);
});
- test('pwinMouseY should be previous vertical position of mouse relative to the window', async function() {
+ test('pwinMouseY should be previous vertical position of mouse relative to the window', async function () {
window.dispatchEvent(mouseEvent1); // dispatch first mouse event
window.dispatchEvent(mouseEvent2); // dispatch second mouse event
assert.strictEqual(myp5.pwinMouseY, mouseEvent1.clientY);
});
- test('pwinMouseY should be previous vertical position of touch relative to the window', function() {
+ test('pwinMouseY should be previous vertical position of touch relative to the window', function () {
window.dispatchEvent(touchEvent1); // dispatch first touch event
window.dispatchEvent(touchEvent2); // dispatch second touch event
assert.strictEqual(myp5.pwinMouseY, touchObj1.clientY);
});
});
- suite('p5.prototype.mouseButton', function() {
- test('_hasMouseInteracted should be a number', function() {
+ suite('p5.prototype.mouseButton', function () {
+ test('_hasMouseInteracted should be a number', function () {
assert.isNumber(myp5.mouseButton);
});
- test('mouseButton should 0 for uninitialised', function() {
+ test('mouseButton should 0 for uninitialised', function () {
assert.strictEqual(myp5.mouseButton, 0);
});
- test('mouseButton should be "left" on left mouse button click', function() {
+ test('mouseButton should be "left" on left mouse button click', function () {
window.dispatchEvent(new PointerEvent('pointerdown', { button: 0 }));
assert.strictEqual(myp5.mouseButton, 'left');
});
- test('mouseButton should be "center" on auxillary mouse button click', function() {
+ test('mouseButton should be "center" on auxillary mouse button click', function () {
window.dispatchEvent(new PointerEvent('pointerdown', { button: 1 }));
assert.strictEqual(myp5.mouseButton, 'center');
});
- test('mouseButton should be "right" on right mouse button click', function() {
+ test('mouseButton should be "right" on right mouse button click', function () {
window.dispatchEvent(new PointerEvent('pointerdown', { button: 2 }));
assert.strictEqual(myp5.mouseButton, 'right');
});
});
- suite('p5.prototype.mouseIsPressed', function() {
- test('mouseIsPressed should be a boolean', function() {
+ suite('p5.prototype.mouseIsPressed', function () {
+ test('mouseIsPressed should be a boolean', function () {
assert.isBoolean(myp5.mouseIsPressed);
});
- test.todo('mouseIsPressed should be false if mouse is not pressed', function() {
+ test.todo('mouseIsPressed should be false if mouse is not pressed', function () {
assert.strictEqual(myp5.mouseIsPressed, false);
});
- test('mouseIsPressed should be true if mouse is pressed', function() {
+ test('mouseIsPressed should be true if mouse is pressed', function () {
window.dispatchEvent(new MouseEvent('mousedown'));
assert.strictEqual(myp5.mouseIsPressed, true);
});
- test('mouseIsPressed should reset to false on blur', function() {
+ test('mouseIsPressed should reset to false on blur', function () {
window.dispatchEvent(new PointerEvent('pointerdown'));
assert.strictEqual(myp5.mouseIsPressed, true);
window.dispatchEvent(new Event('blur'));
@@ -260,11 +266,11 @@ suite.todo('Mouse Events', function() {
});
});
- suite('mouseMoved', function() {
- test.todo('mouseMoved function must run when mouse is moved', async function() {
+ suite('mouseMoved', function () {
+ test.todo('mouseMoved function must run when mouse is moved', async function () {
let count = 0;
- myp5.mouseMoved = function() {
+ myp5.mouseMoved = function () {
count += 1;
};
@@ -273,15 +279,15 @@ suite.todo('Mouse Events', function() {
assert.deepEqual(count, 2);
});
- test('mouseMoved functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('mouseMoved functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.mouseMoved = function() {
+ sketch.mouseMoved = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -294,11 +300,11 @@ suite.todo('Mouse Events', function() {
});
});
- suite('mouseDragged', function() {
- test('mouseDragged function must run when mouse is dragged', async function() {
+ suite('mouseDragged', function () {
+ test('mouseDragged function must run when mouse is dragged', async function () {
let count = 0;
- myp5.mouseDragged = function() {
+ myp5.mouseDragged = function () {
count += 1;
};
@@ -307,15 +313,15 @@ suite.todo('Mouse Events', function() {
assert.deepEqual(count, 1);
});
- test('mouseDragged functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('mouseDragged functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.mouseDragged = function() {
+ sketch.mouseDragged = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -329,11 +335,11 @@ suite.todo('Mouse Events', function() {
});
});
- suite('mousePressed', function() {
- test('mousePressed function must run when mouse is pressed', async function() {
+ suite('mousePressed', function () {
+ test('mousePressed function must run when mouse is pressed', async function () {
let count = 0;
- myp5.mousePressed = function() {
+ myp5.mousePressed = function () {
count += 1;
};
@@ -341,15 +347,15 @@ suite.todo('Mouse Events', function() {
assert.deepEqual(count, 1);
});
- test('mousePressed functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('mousePressed functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.mousePressed = function() {
+ sketch.mousePressed = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -362,11 +368,11 @@ suite.todo('Mouse Events', function() {
});
});
- suite('mouseReleased', function() {
- test('mouseReleased function must run when mouse is released', async function() {
+ suite('mouseReleased', function () {
+ test('mouseReleased function must run when mouse is released', async function () {
let count = 0;
- myp5.mouseReleased = function() {
+ myp5.mouseReleased = function () {
count += 1;
};
@@ -374,15 +380,15 @@ suite.todo('Mouse Events', function() {
assert.deepEqual(count, 1);
});
- test('mouseReleased functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('mouseReleased functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.mouseReleased = function() {
+ sketch.mouseReleased = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -395,11 +401,11 @@ suite.todo('Mouse Events', function() {
});
});
- suite('mouseClicked', function() {
- test('mouseClicked function must run when mouse is clicked', async function() {
+ suite('mouseClicked', function () {
+ test('mouseClicked function must run when mouse is clicked', async function () {
let count = 0;
- myp5.mouseClicked = function() {
+ myp5.mouseClicked = function () {
count += 1;
};
@@ -407,15 +413,15 @@ suite.todo('Mouse Events', function() {
assert.deepEqual(count, 1);
});
- test('mouseClicked functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('mouseClicked functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.mouseClicked = function() {
+ sketch.mouseClicked = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -428,11 +434,11 @@ suite.todo('Mouse Events', function() {
});
});
- suite('doubleClicked', function() {
- test('doubleClicked function must run when mouse is double clicked', async function() {
+ suite('doubleClicked', function () {
+ test('doubleClicked function must run when mouse is double clicked', async function () {
let count = 0;
- myp5.doubleClicked = function() {
+ myp5.doubleClicked = function () {
count += 1;
};
@@ -440,15 +446,15 @@ suite.todo('Mouse Events', function() {
assert.deepEqual(count, 1);
});
- test('doubleClicked functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('doubleClicked functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.doubleClicked = function() {
+ sketch.doubleClicked = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
@@ -461,11 +467,11 @@ suite.todo('Mouse Events', function() {
});
});
- suite('mouseWheel', function() {
- test('mouseWheel function must run when mouse wheel event is detected', async function() {
+ suite('mouseWheel', function () {
+ test('mouseWheel function must run when mouse wheel event is detected', async function () {
let count = 0;
- myp5.mouseWheel = function() {
+ myp5.mouseWheel = function () {
count += 1;
};
@@ -473,15 +479,15 @@ suite.todo('Mouse Events', function() {
assert.deepEqual(count, 1);
});
- test('mouseWheel functions on multiple instances must run once', async function() {
- let sketchFn = function(sketch, resolve, reject) {
+ test('mouseWheel functions on multiple instances must run once', async function () {
+ let sketchFn = function (sketch, resolve, reject) {
let count = 0;
- sketch.mouseWheel = function() {
+ sketch.mouseWheel = function () {
count += 1;
};
- sketch.finish = function() {
+ sketch.finish = function () {
resolve(count);
};
};
diff --git a/test/unit/events/touch.js b/test/unit/events/touch.js
index f1091e5b6e..64528d7178 100644
--- a/test/unit/events/touch.js
+++ b/test/unit/events/touch.js
@@ -1,14 +1,14 @@
import p5 from '../../../src/app.js';
-suite('Touch Events', function() {
+suite('Touch Events', function () {
let myp5;
let touchEvent1;
let touchEvent2;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
touchEvent1 = new PointerEvent('pointerdown', {
pointerId: 1,
@@ -28,36 +28,36 @@ suite('Touch Events', function() {
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- beforeEach(function() {
+ beforeEach(function () {
// Reset pointer state so tests don't leak active pointers into each other.
myp5._activePointers.clear();
myp5.touches = [];
myp5.mouseIsPressed = false;
});
- suite('p5.prototype.touches', function() {
- test('should be an empty array', function() {
+ suite('p5.prototype.touches', function () {
+ test('should be an empty array', function () {
assert.deepEqual(myp5.touches, []);
});
- test('should be an array of multiple touches', function() {
+ test('should be an array of multiple touches', function () {
window.dispatchEvent(touchEvent1);
window.dispatchEvent(touchEvent2);
assert.strictEqual(myp5.touches.length, 2);
});
- test('should contain the touch registered', function() {
+ test('should contain the touch registered', function () {
window.dispatchEvent(touchEvent1);
assert.strictEqual(myp5.touches[0].id, 1);
});
});
- suite('p5.prototype._onpointercancel', function() {
- test('should remove the cancelled touch from touches', function() {
+ suite('p5.prototype._onpointercancel', function () {
+ test('should remove the cancelled touch from touches', function () {
window.dispatchEvent(touchEvent1);
window.dispatchEvent(touchEvent2);
assert.strictEqual(myp5.touches.length, 2);
@@ -76,7 +76,7 @@ suite('Touch Events', function() {
assert.strictEqual(myp5.touches[0].id, 2);
});
- test('should reset mouseIsPressed once all pointers are cancelled', function() {
+ test('should reset mouseIsPressed once all pointers are cancelled', function () {
window.dispatchEvent(touchEvent1);
assert.strictEqual(myp5.mouseIsPressed, true);
diff --git a/test/unit/image/downloading.js b/test/unit/image/downloading.js
index 3d37d979e3..d9956bc68e 100644
--- a/test/unit/image/downloading.js
+++ b/test/unit/image/downloading.js
@@ -12,9 +12,9 @@ const mockAnchorElement = vi.mockObject({
});
const originalCreateElement = document.createElement;
vi.spyOn(document, 'createElement').mockImplementation((...args) => {
- if(args[0] !== 'a'){
+ if (args[0] !== 'a') {
return originalCreateElement.apply(document, args);
- }else{
+ } else {
return mockAnchorElement;
}
});
@@ -66,7 +66,7 @@ const wait = async time => {
};
suite('Downloading', () => {
- beforeAll(async function() {
+ beforeAll(async function () {
image(mockP5, mockP5Prototype);
files(mockP5, mockP5Prototype);
loading(mockP5, mockP5Prototype);
@@ -77,21 +77,21 @@ suite('Downloading', () => {
vi.clearAllMocks();
});
- suite('downloading animated gifs', function() {
+ suite('downloading animated gifs', function () {
let myGif;
const imagePath = '/test/unit/assets/nyan_cat.gif';
- beforeAll(async function() {
+ beforeAll(async function () {
myGif = await mockP5Prototype.loadImage(imagePath);
});
- suite('p5.prototype.encodeAndDownloadGif', function() {
- test('should be a function', function() {
+ suite('p5.prototype.encodeAndDownloadGif', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.encodeAndDownloadGif);
assert.typeOf(mockP5Prototype.encodeAndDownloadGif, 'function');
});
- test('should not throw an error', function() {
+ test('should not throw an error', function () {
mockP5Prototype.encodeAndDownloadGif(myGif);
});
@@ -105,8 +105,8 @@ suite('Downloading', () => {
});
});
- suite('p5.prototype.saveCanvas', function() {
- test('should be a function', function() {
+ suite('p5.prototype.saveCanvas', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.saveCanvas);
assert.typeOf(mockP5Prototype.saveCanvas, 'function');
});
@@ -136,8 +136,8 @@ suite('Downloading', () => {
});
});
- suite('p5.prototype.saveFrames', function() {
- test('should be a function', function() {
+ suite('p5.prototype.saveFrames', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.saveFrames);
assert.typeOf(mockP5Prototype.saveFrames, 'function');
});
@@ -171,8 +171,8 @@ suite('Downloading', () => {
});
});
- suite('p5.prototype.saveGif', function() {
- test('should be a function', function() {
+ suite('p5.prototype.saveGif', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.saveGif);
assert.typeOf(mockP5Prototype.saveGif, 'function');
});
@@ -183,7 +183,10 @@ suite('Downloading', () => {
});
test.todo('should not throw an error', async () => {
- await mockP5Prototype.saveGif('myGif', 3, { delay: 2, frames: 'seconds' });
+ await mockP5Prototype.saveGif('myGif', 3, {
+ delay: 2,
+ frames: 'seconds'
+ });
});
test.todo('should download a GIF', async () => {
diff --git a/test/unit/image/filters.js b/test/unit/image/filters.js
index 9859b220e7..a8c4ac0f8c 100644
--- a/test/unit/image/filters.js
+++ b/test/unit/image/filters.js
@@ -1,12 +1,12 @@
import p5 from '../../../src/app.js';
-suite.todo('Filters', function() {
+suite.todo('Filters', function () {
var myp5;
let img;
- beforeEach(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeEach(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
myp5.createCanvas(10, 10);
img = myp5.createImage(10, 10);
@@ -22,11 +22,11 @@ suite.todo('Filters', function() {
});
});
- afterEach(function() {
+ afterEach(function () {
myp5.remove();
});
- test('threshold filter. less than threshold', function() {
+ test('threshold filter. less than threshold', function () {
img.filter(myp5.THRESHOLD);
myp5.image(img, 0, 0);
myp5.loadPixels();
@@ -37,7 +37,7 @@ suite.todo('Filters', function() {
}
});
- test('threshold filter. greater than threshold', function() {
+ test('threshold filter. greater than threshold', function () {
img.loadPixels();
for (let i = 0; i < img.width; i++) {
for (let j = 0; j < img.height; j++) {
@@ -55,7 +55,7 @@ suite.todo('Filters', function() {
}
});
- test('gray filter', function() {
+ test('gray filter', function () {
img.filter(myp5.GRAY);
myp5.image(img, 0, 0);
myp5.loadPixels();
@@ -65,7 +65,7 @@ suite.todo('Filters', function() {
}
});
- test('opaque filter', function() {
+ test('opaque filter', function () {
img.loadPixels();
for (let i = 0; i < img.width; i++) {
for (let j = 0; j < img.height; j++) {
@@ -81,7 +81,7 @@ suite.todo('Filters', function() {
}
});
- test('invert filter', function() {
+ test('invert filter', function () {
img.filter(myp5.INVERT);
myp5.image(img, 0, 0);
myp5.loadPixels();
diff --git a/test/unit/image/loading.js b/test/unit/image/loading.js
index 94c4c02b77..4b53cbb469 100644
--- a/test/unit/image/loading.js
+++ b/test/unit/image/loading.js
@@ -9,16 +9,16 @@ import { vi } from 'vitest';
* Expects an image file and a p5 instance with an image file loaded and drawn
* and checks that they are exactly the same. Sends result to the callback.
*/
-var testImageRender = function(file, sketch) {
+var testImageRender = function (file, sketch) {
sketch.loadPixels();
var p = sketch.pixels;
var ctx = sketch;
sketch.clear();
- return new Promise(function(resolve, reject) {
+ return new Promise(function (resolve, reject) {
sketch.loadImage(file, resolve, reject);
- }).then(function(img) {
+ }).then(function (img) {
ctx.image(img, 0, 0);
ctx.loadPixels();
@@ -32,7 +32,7 @@ var testImageRender = function(file, sketch) {
});
};
-suite('loading images', function() {
+suite('loading images', function () {
const imagePath = '/test/unit/assets/cat.jpg';
const singleFrameGif = '/test/unit/assets/target_small.gif';
const animatedGif = '/test/unit/assets/white_black.gif';
@@ -43,37 +43,45 @@ suite('loading images', function() {
const svgImage = '/test/unit/assets/green.svg';
const invalidFile = '404file';
- beforeAll(async function() {
+ beforeAll(async function () {
loadingDisplaying(mockP5, mockP5Prototype);
image(mockP5, mockP5Prototype);
await httpMock.start({ quiet: true });
});
test('throws error when encountering HTTP errors', async () => {
- await expect(mockP5Prototype.loadImage(invalidFile))
- .rejects
- .toThrow('Not Found');
+ await expect(mockP5Prototype.loadImage(invalidFile)).rejects.toThrow(
+ 'Not Found'
+ );
});
test('error callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadImage(invalidFile, () => {
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadImage(
+ invalidFile,
+ () => {
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('success callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadImage(imagePath, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- }, err => {
- reject(`Error callback called: ${err.toString()}`);
- });
+ mockP5Prototype.loadImage(
+ imagePath,
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ },
+ err => {
+ reject(`Error callback called: ${err.toString()}`);
+ }
+ );
});
});
@@ -265,7 +273,9 @@ suite('loading images', function() {
// errored load
const INVALID_PATH = '';
- try { await mockP5Prototype.loadImage(INVALID_PATH); } catch {}
+ try {
+ await mockP5Prototype.loadImage(INVALID_PATH);
+ } catch {}
expect(revokeSpy).toHaveBeenCalledTimes(createSpy.mock.calls.length);
createSpy.mockRestore();
@@ -273,26 +283,26 @@ suite('loading images', function() {
});
});
-suite.todo('displaying images', function() {
+suite.todo('displaying images', function () {
var myp5;
var pImg;
var imagePath = 'unit/assets/cat-with-hole.png';
var chanNames = ['red', 'green', 'blue', 'alpha'];
- beforeAll(async function() {
+ beforeAll(async function () {
new Promise(resolve => {
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
myp5.pixelDensity(1);
myp5.loadImage(
imagePath,
- function(img) {
+ function (img) {
pImg = img;
myp5.resizeCanvas(pImg.width, pImg.height);
resolve();
},
- function() {
+ function () {
throw new Error('Error loading image');
}
);
@@ -301,7 +311,7 @@ suite.todo('displaying images', function() {
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
@@ -344,7 +354,7 @@ suite.todo('displaying images', function() {
}
}
- test('tint() with color', function() {
+ test('tint() with color', function () {
assert.ok(pImg, 'image loaded');
var tintColor = [150, 100, 50, 255];
myp5.clear();
@@ -355,7 +365,7 @@ suite.todo('displaying images', function() {
checkTint(tintColor);
});
- test('tint() with alpha', function() {
+ test('tint() with alpha', function () {
assert.ok(pImg, 'image loaded');
var tintColor = [255, 255, 255, 100];
myp5.clear();
@@ -366,7 +376,7 @@ suite.todo('displaying images', function() {
checkTint(tintColor);
});
- test('tint() with color and alpha', function() {
+ test('tint() with color and alpha', function () {
assert.ok(pImg, 'image loaded');
var tintColor = [255, 100, 50, 100];
myp5.clear();
@@ -378,18 +388,18 @@ suite.todo('displaying images', function() {
});
});
-suite('displaying images that use fit mode', function() {
+suite('displaying images that use fit mode', function () {
var myp5, imageSpy;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
@@ -401,7 +411,7 @@ suite('displaying images that use fit mode', function() {
vi.restoreAllMocks();
});
- test('CONTAIN when source image is larger than destination', function() {
+ test('CONTAIN when source image is larger than destination', function () {
let src = myp5.createImage(400, 1000);
myp5.image(src, 0, 0, 300, 400, 0, 0, 400, 1000, myp5.CONTAIN);
expect(imageSpy)
@@ -419,7 +429,7 @@ suite('displaying images that use fit mode', function() {
);
});
- test('CONTAIN when source image is smaller than destination', function() {
+ test('CONTAIN when source image is smaller than destination', function () {
let src = myp5.createImage(40, 90);
myp5.image(src, 0, 0, 300, 500, 0, 0, 400, 1000, myp5.CONTAIN);
expect(imageSpy)
@@ -437,7 +447,7 @@ suite('displaying images that use fit mode', function() {
);
});
- test('COVER when source image is larger than destination', function() {
+ test('COVER when source image is larger than destination', function () {
let src = myp5.createImage(400, 1000);
myp5.image(src, 0, 0, 300, 400, 0, 0, 400, 1000, myp5.COVER);
const r = Math.max(300 / 400, 400 / 1000);
@@ -456,7 +466,7 @@ suite('displaying images that use fit mode', function() {
);
});
- test('COVER when source image is smaller than destination', function() {
+ test('COVER when source image is smaller than destination', function () {
let src = myp5.createImage(20, 100);
myp5.image(src, 0, 0, 300, 400, 0, 0, 20, 100, myp5.COVER);
const r = Math.max(300 / 20, 400 / 100);
diff --git a/test/unit/image/p5.Image.js b/test/unit/image/p5.Image.js
index 5195f2651d..9962a2dfe6 100644
--- a/test/unit/image/p5.Image.js
+++ b/test/unit/image/p5.Image.js
@@ -1,29 +1,29 @@
import p5 from '../../../src/app.js';
-suite('p5.Image', function() {
+suite('p5.Image', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.prototype.createImage', function() {
- test('it creates an image', function() {
+ suite('p5.prototype.createImage', function () {
+ test('it creates an image', function () {
let img = myp5.createImage(10, 17);
assert.isObject(img);
});
});
- suite('p5.Image', function() {
- test('it has necessary properties', function() {
+ suite('p5.Image', function () {
+ test('it has necessary properties', function () {
let img = new p5.Image(100, 100);
assert.property(img, 'width');
assert.property(img, 'height');
@@ -33,7 +33,7 @@ suite('p5.Image', function() {
assert.property(img, 'updatePixels');
});
- test('height and width are correct', function() {
+ test('height and width are correct', function () {
let img = new p5.Image(100, 100);
myp5.pixelDensity(1);
assert.strictEqual(img.width, 100);
@@ -41,8 +41,8 @@ suite('p5.Image', function() {
});
});
- suite('p5.Image.prototype.resize', function() {
- test('it should resize the image', function() {
+ suite('p5.Image.prototype.resize', function () {
+ test('it should resize the image', function () {
let img = myp5.createImage(10, 17);
myp5.pixelDensity(1);
img.resize(10, 30);
@@ -51,9 +51,9 @@ suite('p5.Image', function() {
});
});
- suite.todo('p5.Image.prototype.mask', function() {
+ suite.todo('p5.Image.prototype.mask', function () {
for (const density of [1, 2]) {
- test(`it should mask the image at pixel density ${density}`, function() {
+ test(`it should mask the image at pixel density ${density}`, function () {
let img = myp5.createImage(10, 10);
img.pixelDensity(density);
img.loadPixels();
@@ -87,7 +87,7 @@ suite('p5.Image', function() {
});
}
- test('it should mask images of different density', function() {
+ test('it should mask images of different density', function () {
let img = myp5.createImage(10, 10);
img.pixelDensity(1);
img.loadPixels();
@@ -120,15 +120,15 @@ suite('p5.Image', function() {
}
});
- test('it should mask images from createGraphics', function() {
- myp5.createCanvas(10,10);
+ test('it should mask images from createGraphics', function () {
+ myp5.createCanvas(10, 10);
myp5.pixelDensity(2);
- let img = myp5.createGraphics(10,10);
+ let img = myp5.createGraphics(10, 10);
img.noStroke();
- img.rect(0,0,10,10);
- let mask = myp5.createGraphics(10,10);
+ img.rect(0, 0, 10, 10);
+ let mask = myp5.createGraphics(10, 10);
mask.noStroke();
- mask.rect(0,0,5,5);
+ mask.rect(0, 0, 5, 5);
let masked = img.get();
masked.mask(mask.get());
@@ -140,11 +140,11 @@ suite('p5.Image', function() {
}
});
- test('it should mask the animated gif image', function() {
+ test('it should mask the animated gif image', function () {
const imagePath = 'unit/assets/nyan_cat.gif';
- return new Promise(function(resolve, reject) {
+ return new Promise(function (resolve, reject) {
myp5.loadImage(imagePath, resolve, reject);
- }).then(function(img) {
+ }).then(function (img) {
let mask = myp5.createImage(img.width, img.height);
mask.loadPixels();
for (let i = 0; i < mask.width; i++) {
@@ -181,9 +181,9 @@ suite('p5.Image', function() {
});
});
- suite.todo('p5.Graphics.get()', function() {
+ suite.todo('p5.Graphics.get()', function () {
for (const density of [1, 2]) {
- test(`width and height match at pixel density ${density}`, function() {
+ test(`width and height match at pixel density ${density}`, function () {
const g = myp5.createGraphics(10, 10);
g.pixelDensity(density);
g.rect(2, 2, 5, 5);
diff --git a/test/unit/image/pixels.js b/test/unit/image/pixels.js
index e0b4958c71..f6f970418f 100644
--- a/test/unit/image/pixels.js
+++ b/test/unit/image/pixels.js
@@ -1,24 +1,24 @@
import p5 from '../../../src/app.js';
-suite.todo('pixels', function() {
+suite.todo('pixels', function () {
var myp5;
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
myp5 = p;
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.Image.get', function() {
+ suite('p5.Image.get', function () {
var img;
- beforeAll(function() {
+ beforeAll(function () {
//create a 50 x 50 half red half green image
img = myp5.createImage(50, 50);
img.loadPixels();
@@ -40,21 +40,21 @@ suite.todo('pixels', function() {
img.updatePixels();
});
- test('get(x,y) works with integers', function() {
+ test('get(x,y) works with integers', function () {
assert.deepEqual(img.get(25, 25), [255, 0, 0, 255]);
assert.deepEqual(img.get(25, 26), [0, 0, 255, 255]);
assert.deepEqual(img.get(0, 0), [255, 0, 0, 255]);
assert.deepEqual(img.get(49, 49), [0, 0, 255, 255]);
});
- test('get(x,y) returns 0s for out of bounds arguments', function() {
+ test('get(x,y) returns 0s for out of bounds arguments', function () {
assert.deepEqual(img.get(25, -1), [0, 0, 0, 0]);
assert.deepEqual(img.get(-1, 26), [0, 0, 0, 0]);
assert.deepEqual(img.get(25, 50), [0, 0, 0, 0]);
assert.deepEqual(img.get(50, 26), [0, 0, 0, 0]);
});
- test('get() returns a copy when no arguments are supplied', function() {
+ test('get() returns a copy when no arguments are supplied', function () {
var copy = img.get();
assert.instanceOf(copy, p5.Image);
assert.equal(copy.width, img.width);
@@ -66,7 +66,7 @@ suite.todo('pixels', function() {
assert.deepEqual(copy.get(49, 49), [0, 0, 255, 255]);
});
- test('get(x,y,w,h) works', function() {
+ test('get(x,y,w,h) works', function () {
for (var w = 1; w < img.width + 5; w += 2) {
for (var x = -w * 2; x <= img.width + w * 2; x += 4) {
var copy = img.get(x, x, w, w);
@@ -78,26 +78,26 @@ suite.todo('pixels', function() {
}
});
- test('rounds down when given decimal numbers', function() {
+ test('rounds down when given decimal numbers', function () {
assert.deepEqual(img.get(25, 25.999), img.get(25, 25));
});
});
- suite('p5.Image.pixels', function() {
- test('should be an array of pixels', function() {
+ suite('p5.Image.pixels', function () {
+ test('should be an array of pixels', function () {
let img = myp5.createImage(10, 10);
img.loadPixels();
assert.typeOf(img.pixels, 'Uint8ClampedArray');
});
- test('should store r, g, b, a values for each pixel', function() {
+ test('should store r, g, b, a values for each pixel', function () {
let img = myp5.createImage(10, 10);
myp5.pixelDensity(1);
img.loadPixels();
assert.strictEqual(img.pixels.length, 400);
});
- test('should store correct r, g, b, a values for each pixel', function() {
+ test('should store correct r, g, b, a values for each pixel', function () {
let img = myp5.createImage(10, 10);
myp5.pixelDensity(1);
img.loadPixels();
@@ -114,9 +114,9 @@ suite.todo('pixels', function() {
});
});
- suite('p5.Image.set', function() {
+ suite('p5.Image.set', function () {
/* Parameter Validation missing */
- test('set(x,y) changes color of pixel (x, y)', function() {
+ test('set(x,y) changes color of pixel (x, y)', function () {
let img = myp5.createImage(50, 50);
img.loadPixels();
img.set(0, 0, myp5.color(255, 34, 19));
@@ -125,8 +125,8 @@ suite.todo('pixels', function() {
});
});
- suite('p5.Image.blend', function() {
- test('should copy a region of pixels using the specified blend mode', function() {
+ suite('p5.Image.blend', function () {
+ test('should copy a region of pixels using the specified blend mode', function () {
let img = myp5.createImage(50, 50);
let img2 = myp5.createImage(50, 50);
img.loadPixels();
@@ -154,8 +154,8 @@ suite.todo('pixels', function() {
});
});
- suite('p5.Image.copy', function() {
- test('should copy a region of pixels', function() {
+ suite('p5.Image.copy', function () {
+ test('should copy a region of pixels', function () {
let img = myp5.createImage(50, 50);
let img2 = myp5.createImage(50, 50);
img.loadPixels();
diff --git a/test/unit/io/files.js b/test/unit/io/files.js
index cad7983690..1c30776b8c 100644
--- a/test/unit/io/files.js
+++ b/test/unit/io/files.js
@@ -9,16 +9,16 @@ const mockAnchorElement = vi.mockObject({
});
const originalCreateElement = document.createElement;
vi.spyOn(document, 'createElement').mockImplementation((...args) => {
- if(args[0] !== 'a'){
+ if (args[0] !== 'a') {
return originalCreateElement.apply(document, args);
- }else{
+ } else {
return mockAnchorElement;
}
});
vi.spyOn(URL, 'createObjectURL');
-suite('Files', function() {
- beforeAll(async function() {
+suite('Files', function () {
+ beforeAll(async function () {
files(mockP5, mockP5Prototype);
await httpMock.start({ quiet: true });
});
@@ -28,35 +28,44 @@ suite('Files', function() {
});
// httpDo
- suite('httpDo()', function() {
- test('should work when provided with just a path', async function() {
- const data = await mockP5Prototype.httpDo('/test/unit/assets/sentences.txt');
+ suite('httpDo()', function () {
+ test('should work when provided with just a path', async function () {
+ const data = await mockP5Prototype.httpDo(
+ '/test/unit/assets/sentences.txt'
+ );
assert.ok(data);
assert.isString(data);
});
- test('should accept method parameter', async function() {
- const data = await mockP5Prototype.httpDo('/test/unit/assets/sentences.txt', 'GET');
+ test('should accept method parameter', async function () {
+ const data = await mockP5Prototype.httpDo(
+ '/test/unit/assets/sentences.txt',
+ 'GET'
+ );
assert.ok(data);
assert.isString(data);
});
- test('should accept method and type parameter together', async function() {
- const data = await mockP5Prototype.httpDo('/test/unit/assets/sentences.txt', 'GET', 'text');
+ test('should accept method and type parameter together', async function () {
+ const data = await mockP5Prototype.httpDo(
+ '/test/unit/assets/sentences.txt',
+ 'GET',
+ 'text'
+ );
assert.ok(data);
assert.isString(data);
});
- test('should handle promise error correctly', async function() {
- await expect(mockP5Prototype.httpDo('/test/unit/assets/sen.txt'))
- .rejects
- .toThrow('Not Found');
+ test('should handle promise error correctly', async function () {
+ await expect(
+ mockP5Prototype.httpDo('/test/unit/assets/sen.txt')
+ ).rejects.toThrow('Not Found');
});
});
// saveStrings()
- suite('p5.prototype.saveStrings', function() {
- test('should be a function', function() {
+ suite('p5.prototype.saveStrings', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.saveStrings);
assert.typeOf(mockP5Prototype.saveStrings, 'function');
});
@@ -85,8 +94,8 @@ suite('Files', function() {
});
// saveJSON()
- suite('p5.prototype.saveJSON', function() {
- test('should be a function', function() {
+ suite('p5.prototype.saveJSON', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.saveJSON);
assert.typeOf(mockP5Prototype.saveJSON, 'function');
});
@@ -104,8 +113,8 @@ suite('Files', function() {
});
// writeFile()
- suite('p5.prototype.writeFile', function() {
- test('should be a function', function() {
+ suite('p5.prototype.writeFile', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.writeFile);
assert.typeOf(mockP5Prototype.writeFile, 'function');
});
@@ -123,8 +132,8 @@ suite('Files', function() {
});
// downloadFile()
- suite('p5.prototype.downloadFile', function() {
- test('should be a function', function() {
+ suite('p5.prototype.downloadFile', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.downloadFile);
assert.typeOf(mockP5Prototype.downloadFile, 'function');
});
@@ -143,9 +152,9 @@ suite('Files', function() {
});
// save()
- suite('p5.prototype.save', function() {
- suite('saving images', function() {
- test('should be a function', function() {
+ suite('p5.prototype.save', function () {
+ suite('saving images', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.save);
assert.typeOf(mockP5Prototype.save, 'function');
});
@@ -155,19 +164,22 @@ suite('Files', function() {
test('should call saveCanvas', async () => {
mockP5Prototype.save();
expect(mockP5Prototype.saveCanvas).toHaveBeenCalledTimes(1);
- expect(mockP5Prototype.saveCanvas)
- .toHaveBeenCalledWith(mockP5Prototype.elt);
+ expect(mockP5Prototype.saveCanvas).toHaveBeenCalledWith(
+ mockP5Prototype.elt
+ );
});
test('should call saveCanvas with filename', async () => {
mockP5Prototype.save('filename.jpg');
expect(mockP5Prototype.saveCanvas).toHaveBeenCalledTimes(1);
- expect(mockP5Prototype.saveCanvas)
- .toHaveBeenCalledWith(mockP5Prototype.elt, 'filename.jpg');
+ expect(mockP5Prototype.saveCanvas).toHaveBeenCalledWith(
+ mockP5Prototype.elt,
+ 'filename.jpg'
+ );
});
});
- suite('saving strings and json', function() {
+ suite('saving strings and json', function () {
test('should download a text file', async () => {
const myStrings = ['aaa', 'bbb'];
mockP5Prototype.save(myStrings, 'filename');
diff --git a/test/unit/io/loadBytes.js b/test/unit/io/loadBytes.js
index e5e95b464c..bfa62955d8 100644
--- a/test/unit/io/loadBytes.js
+++ b/test/unit/io/loadBytes.js
@@ -1,7 +1,7 @@
import { mockP5, mockP5Prototype, httpMock } from '../../js/mocks';
import files from '../../../src/io/files';
-suite('loadBytes', function() {
+suite('loadBytes', function () {
const invalidFile = '404file';
const validFile = '/test/unit/assets/nyan_cat.gif';
@@ -11,41 +11,49 @@ suite('loadBytes', function() {
});
test('throws error when encountering HTTP errors', async () => {
- await expect(mockP5Prototype.loadBytes(invalidFile))
- .rejects
- .toThrow('Not Found');
+ await expect(mockP5Prototype.loadBytes(invalidFile)).rejects.toThrow(
+ 'Not Found'
+ );
});
test('error callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadBytes(invalidFile, () => {
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadBytes(
+ invalidFile,
+ () => {
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('success callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadBytes(validFile, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- }, err => {
- reject(`Error callback called: ${err.toString()}`);
- });
+ mockP5Prototype.loadBytes(
+ validFile,
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ },
+ err => {
+ reject(`Error callback called: ${err.toString()}`);
+ }
+ );
});
});
- test('returns the correct object', async () => {
+ test('returns the correct object', async () => {
const data = await mockP5Prototype.loadBytes(validFile);
assert.instanceOf(data, Uint8Array);
// Validate data
const str = 'GIF89a';
// convert the string to a byte array
- const rgb = str.split('').map(function(e) {
+ const rgb = str.split('').map(function (e) {
return e.charCodeAt(0);
});
// this will convert a Uint8Aray to [], if necessary:
@@ -53,14 +61,14 @@ suite('loadBytes', function() {
assert.deepEqual(loaded, rgb);
});
- test('passes athe correct object to success callback', async () => {
+ test('passes athe correct object to success callback', async () => {
await mockP5Prototype.loadBytes(validFile, data => {
assert.instanceOf(data, Uint8Array);
// Validate data
const str = 'GIF89a';
// convert the string to a byte array
- const rgb = str.split('').map(function(e) {
+ const rgb = str.split('').map(function (e) {
return e.charCodeAt(0);
});
// this will convert a Uint8Aray to [], if necessary:
diff --git a/test/unit/io/loadJSON.js b/test/unit/io/loadJSON.js
index ea57d28d33..f0e5b622ef 100644
--- a/test/unit/io/loadJSON.js
+++ b/test/unit/io/loadJSON.js
@@ -1,7 +1,7 @@
import { mockP5, mockP5Prototype, httpMock } from '../../js/mocks';
import files from '../../../src/io/files';
-suite('loadJSON', function() {
+suite('loadJSON', function () {
const invalidFile = '404file';
const jsonArrayFile = '/test/unit/assets/array.json';
const jsonObjectFile = '/test/unit/assets/object.json';
@@ -12,30 +12,38 @@ suite('loadJSON', function() {
});
test('throws error when encountering HTTP errors', async () => {
- await expect(mockP5Prototype.loadJSON(invalidFile))
- .rejects
- .toThrow('Not Found');
+ await expect(mockP5Prototype.loadJSON(invalidFile)).rejects.toThrow(
+ 'Not Found'
+ );
});
test('error callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadJSON(invalidFile, () => {
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadJSON(
+ invalidFile,
+ () => {
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('success callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadJSON(jsonObjectFile, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- }, err => {
- reject(`Error callback called: ${err.toString()}`);
- });
+ mockP5Prototype.loadJSON(
+ jsonObjectFile,
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ },
+ err => {
+ reject(`Error callback called: ${err.toString()}`);
+ }
+ );
});
});
@@ -57,7 +65,7 @@ suite('loadJSON', function() {
assert.lengthOf(data, 3);
});
- test('passes an array to success callback for array JSON.', async function() {
+ test('passes an array to success callback for array JSON.', async function () {
await mockP5Prototype.loadJSON(jsonArrayFile, data => {
assert.isArray(data);
assert.lengthOf(data, 3);
diff --git a/test/unit/io/loadModel.js b/test/unit/io/loadModel.js
index e515af4c22..0c2efa9cf7 100644
--- a/test/unit/io/loadModel.js
+++ b/test/unit/io/loadModel.js
@@ -2,7 +2,7 @@ import { mockP5, mockP5Prototype, httpMock } from '../../js/mocks';
import loading from '../../../src/webgl/loading';
import { Geometry } from '../../../src/webgl/p5.Geometry';
-suite('loadModel', function() {
+suite('loadModel', function () {
const invalidFile = '404file';
const validFile = '/test/unit/assets/teapot.obj';
const validObjFileforMtl = '/test/unit/assets/octa-color.obj';
@@ -19,67 +19,58 @@ suite('loadModel', function() {
});
test('throws error when encountering HTTP errors', async () => {
- await expect(mockP5Prototype.loadModel(invalidFile))
- .rejects
- .toThrow('Not Found');
+ await expect(mockP5Prototype.loadModel(invalidFile)).rejects.toThrow(
+ 'Not Found'
+ );
});
test('error callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadModel(invalidFile, () => {
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadModel(
+ invalidFile,
+ () => {
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('success callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadModel(validFile, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- }, err => {
- reject(`Error callback called: ${err.toString()}`);
- });
+ mockP5Prototype.loadModel(
+ validFile,
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ },
+ err => {
+ reject(`Error callback called: ${err.toString()}`);
+ }
+ );
});
});
- test('loads OBJ file with associated MTL file correctly', async function(){
+ test('loads OBJ file with associated MTL file correctly', async function () {
const model = await mockP5Prototype.loadModel(validObjFileforMtl);
const expectedColors = [
- 0, 0, 0.5, 1,
- 0, 0, 0.5, 1,
- 0, 0, 0.5, 1,
- 0, 0, 0.942654, 1,
- 0, 0, 0.942654, 1,
- 0, 0, 0.942654, 1,
- 0, 0.815632, 1, 1,
- 0, 0.815632, 1, 1,
- 0, 0.815632, 1, 1,
- 0, 0.965177, 1, 1,
- 0, 0.965177, 1, 1,
- 0, 0.965177, 1, 1,
- 0.848654, 1, 0.151346, 1,
- 0.848654, 1, 0.151346, 1,
- 0.848654, 1, 0.151346, 1,
- 1, 0.888635, 0, 1,
- 1, 0.888635, 0, 1,
- 1, 0.888635, 0, 1,
- 1, 0.77791, 0, 1,
- 1, 0.77791, 0, 1,
- 1, 0.77791, 0, 1,
- 0.5, 0, 0, 1,
- 0.5, 0, 0, 1,
- 0.5, 0, 0, 1
+ 0, 0, 0.5, 1, 0, 0, 0.5, 1, 0, 0, 0.5, 1, 0, 0, 0.942654, 1, 0, 0,
+ 0.942654, 1, 0, 0, 0.942654, 1, 0, 0.815632, 1, 1, 0, 0.815632, 1, 1, 0,
+ 0.815632, 1, 1, 0, 0.965177, 1, 1, 0, 0.965177, 1, 1, 0, 0.965177, 1, 1,
+ 0.848654, 1, 0.151346, 1, 0.848654, 1, 0.151346, 1, 0.848654, 1, 0.151346,
+ 1, 1, 0.888635, 0, 1, 1, 0.888635, 0, 1, 1, 0.888635, 0, 1, 1, 0.77791, 0,
+ 1, 1, 0.77791, 0, 1, 1, 0.77791, 0, 1, 0.5, 0, 0, 1, 0.5, 0, 0, 1, 0.5, 0,
+ 0, 1
];
assert.deepEqual(model.vertexColors, expectedColors);
});
- test('splits a multi-material OBJ into one part per material', async function() {
+ test('splits a multi-material OBJ into one part per material', async function () {
const model = await mockP5Prototype.loadModel(validObjFileforMtl);
// octa-color.obj uses 8 materials, one per face
@@ -103,7 +94,7 @@ suite('loadModel', function() {
}
});
- test('loads the diffuse texture (map_Kd) onto the part state', async function() {
+ test('loads the diffuse texture (map_Kd) onto the part state', async function () {
const fakeImage = { width: 1, height: 1 };
mockP5Prototype.loadImage = async url => {
// texture path is resolved relative to the model folder
@@ -111,7 +102,9 @@ suite('loadModel', function() {
return fakeImage;
};
try {
- const model = await mockP5Prototype.loadModel('/test/unit/assets/textured.obj');
+ const model = await mockP5Prototype.loadModel(
+ '/test/unit/assets/textured.obj'
+ );
// two materials, so two parts; the textured one carries the image.
assert.equal(model.parts.length, 2);
const textured = model.parts.find(p => p.partState.texture);
@@ -123,12 +116,14 @@ suite('loadModel', function() {
}
});
- test('a texture that fails to load is skipped without failing the model', async function() {
+ test('a texture that fails to load is skipped without failing the model', async function () {
mockP5Prototype.loadImage = async () => {
throw new Error('Not Found');
};
try {
- const model = await mockP5Prototype.loadModel('/test/unit/assets/textured.obj');
+ const model = await mockP5Prototype.loadModel(
+ '/test/unit/assets/textured.obj'
+ );
assert.equal(model.parts.length, 2);
assert.ok(model.parts.every(p => p.partState.texture == null));
} finally {
@@ -136,7 +131,7 @@ suite('loadModel', function() {
}
});
- test('mixed material coloring loads model with sentinel colors for uncolored vertices', async function() {
+ test('mixed material coloring loads model with sentinel colors for uncolored vertices', async function () {
const model = await mockP5Prototype.loadModel(inconsistentColorObjFile);
assert.instanceOf(model, Geometry);
assert.equal(
@@ -159,51 +154,69 @@ suite('loadModel', function() {
assert.isTrue(hasRealColor, 'Colored vertices should retain their color');
});
- test('missing MTL file shows OBJ model without vertexColors', async function() {
+ test('missing MTL file shows OBJ model without vertexColors', async function () {
const model = await mockP5Prototype.loadModel(objMtlMissing);
assert.instanceOf(model, Geometry);
- assert.equal(model.vertexColors.length, 0, 'Model should not have vertex colors');
+ assert.equal(
+ model.vertexColors.length,
+ 0,
+ 'Model should not have vertex colors'
+ );
});
- test('returns an object with correct data', async function() {
+ test('returns an object with correct data', async function () {
const model = await mockP5Prototype.loadModel(validFile);
assert.instanceOf(model, Geometry);
});
- test('passes an object with correct data to callback', async function() {
+ test('passes an object with correct data to callback', async function () {
await mockP5Prototype.loadModel(validFile, model => {
assert.instanceOf(model, Geometry);
});
});
- test('resolves STL file correctly', async function() {
+ test('resolves STL file correctly', async function () {
const model = await mockP5Prototype.loadModel(validSTLfile);
assert.instanceOf(model, Geometry);
});
- test('resolves STL file correctly with explicit extension', async function() {
- const model = await mockP5Prototype.loadModel(validSTLfileWithoutExtension, '.stl');
+ test('resolves STL file correctly with explicit extension', async function () {
+ const model = await mockP5Prototype.loadModel(
+ validSTLfileWithoutExtension,
+ '.stl'
+ );
assert.instanceOf(model, Geometry);
});
- test('resolves STL file correctly with case insensitive extension', async function() {
- const model = await mockP5Prototype.loadModel(validSTLfileWithoutExtension, '.STL');
+ test('resolves STL file correctly with case insensitive extension', async function () {
+ const model = await mockP5Prototype.loadModel(
+ validSTLfileWithoutExtension,
+ '.STL'
+ );
assert.instanceOf(model, Geometry);
});
- test('OBJ with negative vertex indices loads correctly', async function() {
+ test('OBJ with negative vertex indices loads correctly', async function () {
const model = await mockP5Prototype.loadModel(negativeIndexCubeFile);
assert.instanceOf(model, Geometry);
assert.isAbove(model.vertices.length, 0, 'Model should have vertices');
assert.isAbove(model.faces.length, 0, 'Model should have faces');
});
- test('OBJ negative indices produce same geometry as positive', async function() {
+ test('OBJ negative indices produce same geometry as positive', async function () {
const positiveModel = await mockP5Prototype.loadModel(validCubeFile);
- const negativeModel = await mockP5Prototype.loadModel(negativeIndexCubeFile);
- assert.equal(positiveModel.vertices.length, negativeModel.vertices.length,
- 'Vertex count should match');
- assert.equal(positiveModel.faces.length, negativeModel.faces.length,
- 'Face count should match');
+ const negativeModel = await mockP5Prototype.loadModel(
+ negativeIndexCubeFile
+ );
+ assert.equal(
+ positiveModel.vertices.length,
+ negativeModel.vertices.length,
+ 'Vertex count should match'
+ );
+ assert.equal(
+ positiveModel.faces.length,
+ negativeModel.faces.length,
+ 'Face count should match'
+ );
});
});
diff --git a/test/unit/io/loadShader.js b/test/unit/io/loadShader.js
index 7894f54b39..e2888171d8 100644
--- a/test/unit/io/loadShader.js
+++ b/test/unit/io/loadShader.js
@@ -2,7 +2,7 @@ import { mockP5, mockP5Prototype, httpMock } from '../../js/mocks';
import material from '../../../src/webgl/material';
import { Shader } from '../../../src/webgl/p5.Shader';
-suite('loadShader', function() {
+suite('loadShader', function () {
const invalidFile = '404file';
const vertFile = '/test/unit/assets/vert.glsl';
const fragFile = '/test/unit/assets/frag.glsl';
@@ -13,56 +13,71 @@ suite('loadShader', function() {
});
test('throws error when encountering HTTP errors in vert shader', async () => {
- await expect(mockP5Prototype.loadShader(invalidFile, fragFile))
- .rejects
- .toThrow('Not Found');
+ await expect(
+ mockP5Prototype.loadShader(invalidFile, fragFile)
+ ).rejects.toThrow('Not Found');
});
test('throws error when encountering HTTP errors in frag shader', async () => {
- await expect(mockP5Prototype.loadShader(vertFile, invalidFile))
- .rejects
- .toThrow('Not Found');
+ await expect(
+ mockP5Prototype.loadShader(vertFile, invalidFile)
+ ).rejects.toThrow('Not Found');
});
test('error callback is called for vert shader', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadShader(invalidFile, fragFile, () => {
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadShader(
+ invalidFile,
+ fragFile,
+ () => {
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('error callback is called for frag shader', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadShader(vertFile, invalidFile, () => {
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadShader(
+ vertFile,
+ invalidFile,
+ () => {
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('success callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadShader(vertFile, fragFile, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- }, err => {
- reject(`Error callback called: ${err.toString()}`);
- });
+ mockP5Prototype.loadShader(
+ vertFile,
+ fragFile,
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ },
+ err => {
+ reject(`Error callback called: ${err.toString()}`);
+ }
+ );
});
});
- test('returns an object with correct data', async function() {
+ test('returns an object with correct data', async function () {
const shader = await mockP5Prototype.loadShader(vertFile, fragFile);
assert.instanceOf(shader, Shader);
});
- test('passes an object with correct data to callback', async function() {
+ test('passes an object with correct data to callback', async function () {
await mockP5Prototype.loadShader(vertFile, fragFile, shader => {
assert.instanceOf(shader, Shader);
});
diff --git a/test/unit/io/loadStrings.js b/test/unit/io/loadStrings.js
index c8916b97a0..45795b1dfb 100644
--- a/test/unit/io/loadStrings.js
+++ b/test/unit/io/loadStrings.js
@@ -1,7 +1,7 @@
import { mockP5, mockP5Prototype, httpMock } from '../../js/mocks';
import files from '../../../src/io/files';
-suite('loadStrings', function() {
+suite('loadStrings', function () {
const invalidFile = '404file';
const validFile = '/test/unit/assets/sentences.txt';
const fileWithEmptyLines = '/test/unit/assets/empty_lines.txt';
@@ -13,57 +13,65 @@ suite('loadStrings', function() {
});
test('throws error when encountering HTTP errors', async () => {
- await expect(mockP5Prototype.loadStrings(invalidFile))
- .rejects
- .toThrow('Not Found');
+ await expect(mockP5Prototype.loadStrings(invalidFile)).rejects.toThrow(
+ 'Not Found'
+ );
});
test('error callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadStrings(invalidFile, () => {
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadStrings(
+ invalidFile,
+ () => {
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('success callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadStrings(validFile, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- }, err => {
- reject(`Error callback called: ${err.toString()}`);
- });
+ mockP5Prototype.loadStrings(
+ validFile,
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ },
+ err => {
+ reject(`Error callback called: ${err.toString()}`);
+ }
+ );
});
});
- test('returns an array of strings', async () => {
+ test('returns an array of strings', async () => {
const strings = await mockP5Prototype.loadStrings(validFile);
assert.isArray(strings);
- for(let string of strings){
+ for (let string of strings) {
assert.isString(string);
}
});
- test('passes an array to success callback', async () => {
+ test('passes an array to success callback', async () => {
await mockP5Prototype.loadStrings(validFile, strings => {
assert.isArray(strings);
- for(let string of strings){
+ for (let string of strings) {
assert.isString(string);
}
});
});
- test('should include empty strings', async () => {
+ test('should include empty strings', async () => {
const strings = await mockP5Prototype.loadStrings(fileWithEmptyLines);
assert.isArray(strings, 'Array passed to callback function');
assert.lengthOf(strings, 6, 'length of data is 6');
});
- test('can load file with many lines', async () => {
+ test('can load file with many lines', async () => {
const strings = await mockP5Prototype.loadStrings(fileWithManyLines);
assert.isArray(strings, 'Array passed to callback function');
assert.lengthOf(strings, 131073, 'length of data is 131073');
diff --git a/test/unit/io/loadTable.js b/test/unit/io/loadTable.js
index ef22619be6..819e4d4f16 100644
--- a/test/unit/io/loadTable.js
+++ b/test/unit/io/loadTable.js
@@ -3,7 +3,7 @@ import files from '../../../src/io/files';
import table from '../../../src/io/p5.Table';
import tableRow from '../../../src/io/p5.TableRow';
-suite('loadTable', function() {
+suite('loadTable', function () {
const invalidFile = '404file';
const validFile = '/test/unit/assets/csv.csv';
@@ -15,30 +15,38 @@ suite('loadTable', function() {
});
test('throws error when encountering HTTP errors', async () => {
- await expect(mockP5Prototype.loadTable(invalidFile))
- .rejects
- .toThrow('Not Found');
+ await expect(mockP5Prototype.loadTable(invalidFile)).rejects.toThrow(
+ 'Not Found'
+ );
});
test('error callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadTable(invalidFile, () => {
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadTable(
+ invalidFile,
+ () => {
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('success callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadTable(validFile, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- }, err => {
- reject(`Error callback called: ${err.toString()}`);
- });
+ mockP5Prototype.loadTable(
+ validFile,
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ },
+ err => {
+ reject(`Error callback called: ${err.toString()}`);
+ }
+ );
});
});
diff --git a/test/unit/io/loadXML.js b/test/unit/io/loadXML.js
index 2715d22d4c..1ee1dbb929 100644
--- a/test/unit/io/loadXML.js
+++ b/test/unit/io/loadXML.js
@@ -2,7 +2,7 @@ import { mockP5, mockP5Prototype, httpMock } from '../../js/mocks';
import files from '../../../src/io/files';
import xml from '../../../src/io/p5.XML';
-suite('loadXML', function() {
+suite('loadXML', function () {
const invalidFile = '404file';
const validFile = '/test/unit/assets/books.xml';
@@ -13,31 +13,39 @@ suite('loadXML', function() {
});
test('throws error when encountering HTTP errors', async () => {
- await expect(mockP5Prototype.loadXML(invalidFile))
- .rejects
- .toThrow('Not Found');
+ await expect(mockP5Prototype.loadXML(invalidFile)).rejects.toThrow(
+ 'Not Found'
+ );
});
test('error callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadXML(invalidFile, () => {
- console.log('here');
- reject('Success callback executed');
- }, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- });
+ mockP5Prototype.loadXML(
+ invalidFile,
+ () => {
+ console.log('here');
+ reject('Success callback executed');
+ },
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ }
+ );
});
});
test('success callback is called', async () => {
await new Promise((resolve, reject) => {
- mockP5Prototype.loadXML(validFile, () => {
- // Wait a bit so that if both callbacks are executed we will get an error.
- setTimeout(resolve, 50);
- }, err => {
- reject(`Error callback called: ${err.toString()}`);
- });
+ mockP5Prototype.loadXML(
+ validFile,
+ () => {
+ // Wait a bit so that if both callbacks are executed we will get an error.
+ setTimeout(resolve, 50);
+ },
+ err => {
+ reject(`Error callback called: ${err.toString()}`);
+ }
+ );
});
});
diff --git a/test/unit/io/parseMtl.js b/test/unit/io/parseMtl.js
index c7956ca414..c01ef7947a 100644
--- a/test/unit/io/parseMtl.js
+++ b/test/unit/io/parseMtl.js
@@ -1,7 +1,7 @@
import { parseMtlData, mtlToPartState } from '../../../src/webgl/loading';
-suite('parseMtlData', function() {
- test('parses a full set of material tokens', function() {
+suite('parseMtlData', function () {
+ test('parses a full set of material tokens', function () {
const mtl = [
'newmtl shiny',
'Kd 0.1 0.2 0.3',
@@ -32,28 +32,26 @@ suite('parseMtlData', function() {
expect(m.bumpTexturePath).toEqual('bump.png');
});
- test('Tr is read as the inverse of d', function() {
+ test('Tr is read as the inverse of d', function () {
const materials = parseMtlData('newmtl glass\nTr 0.25');
expect(materials.glass.opacity).toEqual(0.75);
});
- test('keeps each material separate', function() {
- const materials = parseMtlData(
- 'newmtl a\nKd 1 0 0\nnewmtl b\nKd 0 1 0'
- );
+ test('keeps each material separate', function () {
+ const materials = parseMtlData('newmtl a\nKd 1 0 0\nnewmtl b\nKd 0 1 0');
expect(materials.a.diffuseColor).toEqual([1, 0, 0]);
expect(materials.b.diffuseColor).toEqual([0, 1, 0]);
});
- test('ignores lines before any newmtl', function() {
+ test('ignores lines before any newmtl', function () {
const materials = parseMtlData('Kd 1 1 1\nnewmtl a\nKd 0 0 0');
expect(materials.a.diffuseColor).toEqual([0, 0, 0]);
expect(Object.keys(materials)).toEqual(['a']);
});
});
-suite('mtlToPartState', function() {
- test('maps mtl fields onto p5 part-state vocabulary', function() {
+suite('mtlToPartState', function () {
+ test('maps mtl fields onto p5 part-state vocabulary', function () {
const state = mtlToPartState({
diffuseColor: [1, 0, 0],
ambientColor: [0, 1, 0],
@@ -66,7 +64,7 @@ suite('mtlToPartState', function() {
expect(state.shininess).toEqual(32);
});
- test('returns an all-null state for a missing material', function() {
+ test('returns an all-null state for a missing material', function () {
const state = mtlToPartState(undefined);
expect(state.fill).toBeNull();
expect(state.texture).toBeNull();
diff --git a/test/unit/io/saveTable.js b/test/unit/io/saveTable.js
index 691432d53e..8e12a6e82d 100644
--- a/test/unit/io/saveTable.js
+++ b/test/unit/io/saveTable.js
@@ -11,18 +11,18 @@ const mockAnchorElement = vi.mockObject({
});
const originalCreateElement = document.createElement;
vi.spyOn(document, 'createElement').mockImplementation((...args) => {
- if(args[0] !== 'a'){
+ if (args[0] !== 'a') {
return originalCreateElement.apply(document, args);
- }else{
+ } else {
return mockAnchorElement;
}
});
-suite('saveTable', function() {
+suite('saveTable', function () {
const validFile = '/test/unit/assets/csv.csv';
let myTable;
- beforeAll(async function() {
+ beforeAll(async function () {
files(mockP5, mockP5Prototype);
table(mockP5, mockP5Prototype);
tableRow(mockP5, mockP5Prototype);
@@ -33,7 +33,7 @@ suite('saveTable', function() {
vi.clearAllMocks();
});
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.saveTable);
assert.typeOf(mockP5Prototype.saveTable, 'function');
});
diff --git a/test/unit/math/calculation.js b/test/unit/math/calculation.js
index 82868210b8..5424c1ab23 100644
--- a/test/unit/math/calculation.js
+++ b/test/unit/math/calculation.js
@@ -1,472 +1,477 @@
import calculation from '../../../src/math/calculation.js';
import { vi } from 'vitest';
-suite('Calculation', function() {
+suite('Calculation', function () {
const mockP5 = {
_validateParameters: vi.fn()
};
const mockP5Prototype = {};
- beforeAll(function() {
+ beforeAll(function () {
calculation(mockP5, mockP5Prototype);
});
- afterAll(function() {
- });
+ afterAll(function () {});
- suite('p5.prototype.abs', function() {
+ suite('p5.prototype.abs', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.abs);
assert.typeOf(mockP5Prototype.abs, 'function');
});
- test('should return a number', function() {
+ test('should return a number', function () {
result = mockP5Prototype.abs();
assert.typeOf(result, 'number');
});
- test('should return an absolute value', function() {
+ test('should return an absolute value', function () {
result = mockP5Prototype.abs(-1);
assert.equal(result, 1);
assert.notEqual(result, -1);
});
});
- suite('p5.prototype.ceil', function() {
+ suite('p5.prototype.ceil', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.ceil);
assert.typeOf(mockP5Prototype.ceil, 'function');
});
- test('should return ceil value given negative value', function() {
+ test('should return ceil value given negative value', function () {
result = mockP5Prototype.ceil(-1.9);
assert.equal(result, -1);
});
- test('should return a ceil value given positive value', function() {
+ test('should return a ceil value given positive value', function () {
result = mockP5Prototype.ceil(0.1);
assert.equal(result, 1);
});
- test('should return same number', function() {
+ test('should return same number', function () {
result = mockP5Prototype.ceil(1);
assert.equal(result, 1);
});
});
- suite('p5.prototype.dist', function() {
+ suite('p5.prototype.dist', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.dist);
assert.typeOf(mockP5Prototype.dist, 'function');
});
- test('should return a number', function() {
+ test('should return a number', function () {
result = mockP5Prototype.dist(0, 0, 2, 3);
assert.typeOf(result, 'number');
});
- test('should return correct distance', function() {
+ test('should return correct distance', function () {
result = mockP5Prototype.dist(0, 0, 2, 3);
assert.approximately(result, 3.605551, 0.000001); // Math.hypot(2, 3)
});
- test('should return positive distance', function() {
+ test('should return positive distance', function () {
result = mockP5Prototype.dist(0, 0, -2, -3);
assert.approximately(result, 3.605551, 0.000001); // Math.hypot(2, 3)
});
- test('should return correct distance', function() {
+ test('should return correct distance', function () {
result = mockP5Prototype.dist(0, 0, 0, 2, 3, 5);
assert.approximately(result, 6.164414, 0.000001); // Math.hypot(2, 3, 5)
});
- test('should return positive distance', function() {
+ test('should return positive distance', function () {
result = mockP5Prototype.dist(0, 0, 0, -2, -3, 5);
assert.approximately(result, 6.164414, 0.000001); // Math.hypot(2, 3, 5)
});
- test('should not underflow', function() {
+ test('should not underflow', function () {
result = mockP5Prototype.dist(0, 0, 1e-200, 2e-200);
assert.notEqual(result, 0);
});
- test('should not overflow', function() {
+ test('should not overflow', function () {
result = mockP5Prototype.dist(0, 0, 1e200, 2e200);
assert.notEqual(result, Infinity);
});
- test('should return 0 for identical 2D points', function() {
+ test('should return 0 for identical 2D points', function () {
result = mockP5Prototype.dist(2, 3, 2, 3);
assert.equal(result, 0);
});
- test('should return 0 for identical 3D points', function() {
+ test('should return 0 for identical 3D points', function () {
result = mockP5Prototype.dist(2, 3, 5, 2, 3, 5);
assert.equal(result, 0);
});
- test('should return infinity if coordinate of a point is at infinity (2D)', function() {
+ test('should return infinity if coordinate of a point is at infinity (2D)', function () {
result = mockP5Prototype.dist(0, 0, Infinity, 0);
assert.equal(result, Infinity);
});
- test('should return infinity if coordinate of a point is at -infinity (2D)', function() {
+ test('should return infinity if coordinate of a point is at -infinity (2D)', function () {
result = mockP5Prototype.dist(0, 0, -Infinity, 0);
assert.equal(result, Infinity);
});
- test('should handle overflow correctly (2D)', function() {
+ test('should handle overflow correctly (2D)', function () {
result = mockP5Prototype.dist(0, 1e200, 0, 1e199);
assert.equal(result, 9e199);
});
- test('should handle rounding correctly (2D)', function() {
+ test('should handle rounding correctly (2D)', function () {
result = mockP5Prototype.dist(0, 1e-200, 0, 1e-199);
assert.equal(result, 9e-200);
});
- test('should handle string parameters correctly (2D)', function() {
+ test('should handle string parameters correctly (2D)', function () {
result = mockP5Prototype.dist(0, 0, '4', '3');
assert.equal(result, 5);
});
- test('should return infinity if coordinate of a point is at infinity (3D)', function() {
+ test('should return infinity if coordinate of a point is at infinity (3D)', function () {
result = mockP5Prototype.dist(0, 0, 0, Infinity, 0, 0);
assert.equal(result, Infinity);
});
- test('should return infinity if coordinate of a point is at -infinity (3D)', function() {
+ test('should return infinity if coordinate of a point is at -infinity (3D)', function () {
result = mockP5Prototype.dist(0, 0, 0, -Infinity, 0, 0);
assert.equal(result, Infinity);
});
- test('should handle overflow correctly (3D)', function() {
+ test('should handle overflow correctly (3D)', function () {
result = mockP5Prototype.dist(0, 0, 1e200, 0, 0, 1e199);
assert.equal(result, 9e199);
});
- test('should handle rounding correctly (3D)', function() {
+ test('should handle rounding correctly (3D)', function () {
result = mockP5Prototype.dist(0, 0, 1e-200, 0, 0, 1e-199);
assert.equal(result, 9e-200);
});
- test('should handle string parameters correctly (3D)', function() {
+ test('should handle string parameters correctly (3D)', function () {
result = mockP5Prototype.dist(0, 0, 0, '4', '4', '2');
assert.equal(result, 6);
});
});
- suite('p5.prototype.exp', function() {
+ suite('p5.prototype.exp', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.exp);
assert.typeOf(mockP5Prototype.exp, 'function');
});
- test('should return exp value given negative value', function() {
+ test('should return exp value given negative value', function () {
result = mockP5Prototype.exp(-1);
assert.approximately(result, Math.exp(-1), 0.000001);
});
- test('should return exp value given positive value', function() {
+ test('should return exp value given positive value', function () {
result = mockP5Prototype.exp(1);
assert.approximately(result, Math.exp(1), 0.000001);
});
- test('should return 1', function() {
+ test('should return 1', function () {
result = mockP5Prototype.exp(0);
assert.equal(result, 1);
});
});
- suite('p5.prototype.floor', function() {
+ suite('p5.prototype.floor', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.floor);
assert.typeOf(mockP5Prototype.floor, 'function');
});
- test('should return floor value given negative value', function() {
+ test('should return floor value given negative value', function () {
result = mockP5Prototype.floor(-1.9);
assert.equal(result, -2);
});
- test('should return a floor value given positive value', function() {
+ test('should return a floor value given positive value', function () {
result = mockP5Prototype.floor(0.1);
assert.equal(result, 0);
});
- test('should return same number', function() {
+ test('should return same number', function () {
result = mockP5Prototype.floor(1);
assert.equal(result, 1);
});
});
- suite('p5.prototype.lerp', function() {
+ suite('p5.prototype.lerp', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.lerp);
assert.typeOf(mockP5Prototype.lerp, 'function');
});
- test('should return start', function() {
+ test('should return start', function () {
result = mockP5Prototype.lerp(0, 5, 0);
assert.equal(result, 0);
});
- test('should return average', function() {
+ test('should return average', function () {
result = mockP5Prototype.lerp(0, 5, 0.5);
assert.equal(result, 2.5);
});
- test('should return stop', function() {
+ test('should return stop', function () {
result = mockP5Prototype.lerp(0, 5, 1);
assert.equal(result, 5);
});
});
- suite('p5.prototype.log', function() {
+ suite('p5.prototype.log', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.log);
assert.typeOf(mockP5Prototype.log, 'function');
});
- test('should return log value given negative value', function() {
+ test('should return log value given negative value', function () {
result = mockP5Prototype.log(Math.exp(-1));
assert.approximately(result, -1, 0.0001);
});
- test('should return log value given positive value', function() {
+ test('should return log value given positive value', function () {
result = mockP5Prototype.log(Math.exp(1));
assert.approximately(result, 1, 0.0001);
});
- test('should return 0', function() {
+ test('should return 0', function () {
result = mockP5Prototype.log(Math.exp(0));
assert.equal(result, 0);
});
});
- suite('p5.prototype.mag', function() {
+ suite('p5.prototype.mag', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.mag);
assert.typeOf(mockP5Prototype.mag, 'function');
});
- test('should return a number', function() {
+ test('should return a number', function () {
result = mockP5Prototype.mag(2, 3);
assert.typeOf(result, 'number');
});
- test('should return correct magitude', function() {
+ test('should return correct magitude', function () {
result = mockP5Prototype.mag(2, 3);
assert.approximately(result, 3.605551, 0.000001); // Math.hypot(2, 3)
});
- test('should return positive magnitude given negative inputs', function() {
+ test('should return positive magnitude given negative inputs', function () {
result = mockP5Prototype.mag(-2, -3);
assert.approximately(result, 3.605551, 0.000001); // Math.hypot(2, 3)
});
});
- suite('p5.prototype.map', function() {
+ suite('p5.prototype.map', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.map);
assert.typeOf(mockP5Prototype.map, 'function');
});
- test('should return a number', function() {
+ test('should return a number', function () {
result = mockP5Prototype.map(1, 0, 10, 0, 20);
assert.typeOf(result, 'number');
});
- test('should return scaled value', function() {
+ test('should return scaled value', function () {
result = mockP5Prototype.map(1, 0, 10, 0, 20);
assert.equal(result, 2);
});
- test('should extrapolate by default', function() {
+ test('should extrapolate by default', function () {
assert.approximately(mockP5Prototype.map(10, 0, 1, 10, 11), 20, 0.01);
assert.approximately(mockP5Prototype.map(-1, 0, 1, 10, 11), 9, 0.01);
assert.approximately(mockP5Prototype.map(2, 0, 1, 20, 10), 0, 0.01);
});
- test('shaould clamp correctly', function() {
+ test('shaould clamp correctly', function () {
assert.approximately(mockP5Prototype.map(1, 0, 10, 0, 20, true), 2, 0.01);
assert.approximately(
- mockP5Prototype.map(10, 0, 1, 10, 11, true), 11, 0.01
+ mockP5Prototype.map(10, 0, 1, 10, 11, true),
+ 11,
+ 0.01
);
assert.approximately(
- mockP5Prototype.map(-1, 0, 1, 10, 11, true), 10, 0.01
+ mockP5Prototype.map(-1, 0, 1, 10, 11, true),
+ 10,
+ 0.01
);
assert.approximately(
- mockP5Prototype.map(2, 0, 1, 20, 10, true), 10, 0.01
+ mockP5Prototype.map(2, 0, 1, 20, 10, true),
+ 10,
+ 0.01
);
});
});
- suite('p5.prototype.max', function() {
+ suite('p5.prototype.max', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.max);
assert.typeOf(mockP5Prototype.max, 'function');
});
- test('should return larger left argument', function() {
+ test('should return larger left argument', function () {
result = mockP5Prototype.max(10, -1);
assert.equal(result, 10);
});
- test('should return larger right argument', function() {
+ test('should return larger right argument', function () {
result = mockP5Prototype.max(-1, 10);
assert.equal(result, 10);
});
- test('should return single value', function() {
+ test('should return single value', function () {
result = mockP5Prototype.max(10, 10);
assert.equal(result, 10);
});
- test('should return larger value from array', function() {
+ test('should return larger value from array', function () {
result = mockP5Prototype.max([10, -1]);
assert.equal(result, 10);
});
- test('should return larger value from array', function() {
+ test('should return larger value from array', function () {
result = mockP5Prototype.max(-1, 10);
assert.equal(result, 10);
});
- test('should return single value from array', function() {
+ test('should return single value from array', function () {
result = mockP5Prototype.max([10, 10]);
assert.equal(result, 10);
});
- test('should handle Infinity as a valid argument', function() {
+ test('should handle Infinity as a valid argument', function () {
result = mockP5Prototype.max(Infinity, 42);
assert.equal(result, Infinity);
});
- test('should handle -Infinity as a valid argument', function() {
+ test('should handle -Infinity as a valid argument', function () {
result = mockP5Prototype.max(-Infinity, 42);
assert.equal(result, 42);
});
});
- suite('p5.prototype.min', function() {
+ suite('p5.prototype.min', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.min);
assert.typeOf(mockP5Prototype.min, 'function');
});
- test('should return smaller right argument', function() {
+ test('should return smaller right argument', function () {
result = mockP5Prototype.min(10, -1);
assert.equal(result, -1);
});
- test('should return smaller left argument', function() {
+ test('should return smaller left argument', function () {
result = mockP5Prototype.min(-1, 10);
assert.equal(result, -1);
});
- test('should return single value', function() {
+ test('should return single value', function () {
result = mockP5Prototype.min(10, 10);
assert.equal(result, 10);
});
- test('should return smaller value from array', function() {
+ test('should return smaller value from array', function () {
result = mockP5Prototype.min([10, -1]);
assert.equal(result, -1);
});
- test('should return smaller value from array', function() {
+ test('should return smaller value from array', function () {
result = mockP5Prototype.min([-1, 10]);
assert.equal(result, -1);
});
- test('should return single value from array', function() {
+ test('should return single value from array', function () {
result = mockP5Prototype.min([10, 10]);
assert.equal(result, 10);
});
- test('should handle Infinity as a valid argument', function() {
+ test('should handle Infinity as a valid argument', function () {
result = mockP5Prototype.min(Infinity, 42);
assert.equal(result, 42);
});
- test('should handle -Infinity as a valid argument', function() {
+ test('should handle -Infinity as a valid argument', function () {
result = mockP5Prototype.min(-Infinity, 42);
assert.equal(result, -Infinity);
});
});
- suite('p5.prototype.norm', function() {
+ suite('p5.prototype.norm', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.norm);
assert.typeOf(mockP5Prototype.norm, 'function');
});
- test('should return scaled decimal value', function() {
+ test('should return scaled decimal value', function () {
// note: there is currently scoping issues with "this" keyword
result = mockP5Prototype.norm(20, 0, 50);
assert.equal(result, 0.4);
});
});
- suite('p5.prototype.constrain', function() {
+ suite('p5.prototype.constrain', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.constrain);
assert.typeOf(mockP5Prototype.constrain, 'function');
});
- test('should return same number', function() {
+ test('should return same number', function () {
result = mockP5Prototype.constrain(1, 3, 5);
assert.equal(result, 3);
});
- test('should return lower bound', function() {
+ test('should return lower bound', function () {
result = mockP5Prototype.constrain(1, -1, 5);
assert.equal(result, 1);
});
- test('should return upper bound', function() {
+ test('should return upper bound', function () {
result = mockP5Prototype.constrain(1, 10, 5);
assert.equal(result, 10);
});
});
- suite('p5.prototype.sq', function() {
+ suite('p5.prototype.sq', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.sq);
assert.typeOf(mockP5Prototype.sq, 'function');
});
- test('should return sauare value', function() {
+ test('should return sauare value', function () {
result = mockP5Prototype.sq(10);
assert.equal(result, 100);
});
- test('should return squared value given negative number', function() {
+ test('should return squared value given negative number', function () {
result = mockP5Prototype.sq(-10);
assert.equal(result, 100);
});
});
- suite('p5.prototype.pow', function() {
+ suite('p5.prototype.pow', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.pow);
assert.typeOf(mockP5Prototype.pow, 'function');
});
- test('should return pow for negative exponential', function() {
+ test('should return pow for negative exponential', function () {
result = mockP5Prototype.pow(2, -1);
assert.equal(result, 0.5);
});
- test('should return pow for positive exponential', function() {
+ test('should return pow for positive exponential', function () {
result = mockP5Prototype.pow(2, 4);
assert.equal(result, 16);
});
});
- suite('p5.prototype.round', function() {
+ suite('p5.prototype.round', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.round);
assert.typeOf(mockP5Prototype.round, 'function');
});
- test('should round down', function() {
+ test('should round down', function () {
result = mockP5Prototype.round(2.1);
assert.equal(result, 2);
});
- test('should round up from midpoint', function() {
+ test('should round up from midpoint', function () {
result = mockP5Prototype.round(2.5);
assert.equal(result, 3);
});
- test('should round up', function() {
+ test('should round up', function () {
result = mockP5Prototype.round(2.8);
assert.equal(result, 3);
});
- test('should round two decimal places', function() {
+ test('should round two decimal places', function () {
result = mockP5Prototype.round(12.31833, 2);
assert.equal(result, 12.32);
});
- test('should round very small numbers to zero', function() {
+ test('should round very small numbers to zero', function () {
result = mockP5Prototype.round(1.234567e-14);
assert.equal(result, 0);
});
- test('should round very small numbers to zero when decimal places are specified', function() {
+ test('should round very small numbers to zero when decimal places are specified', function () {
result = mockP5Prototype.round(1.234567e-14, 2);
assert.equal(result, 0);
});
});
- suite('p5.prototype.sqrt', function() {
+ suite('p5.prototype.sqrt', function () {
var result;
- test('should be a function', function() {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.sqrt);
assert.typeOf(mockP5Prototype.sqrt, 'function');
});
- test('should return square root', function() {
+ test('should return square root', function () {
result = mockP5Prototype.sqrt(100);
assert.equal(result, 10);
});
diff --git a/test/unit/math/noise.js b/test/unit/math/noise.js
index 3b95dd403f..16ebdc1afa 100644
--- a/test/unit/math/noise.js
+++ b/test/unit/math/noise.js
@@ -1,18 +1,17 @@
import noise from '../../../src/math/noise.js';
import { vi } from 'vitest';
-suite('Noise', function() {
+suite('Noise', function () {
const mockP5 = {
_validateParameters: vi.fn()
};
const mockP5Prototype = {};
- beforeAll(function() {
+ beforeAll(function () {
noise(mockP5, mockP5Prototype);
});
- afterAll(function() {
- });
+ afterAll(function () {});
// This could use some better testing!
// Just checking that we get an actual number now
@@ -21,22 +20,22 @@ suite('Noise', function() {
var result;
var results = [];
- suite('p5.prototype.noise', function() {
- beforeEach(function() {
+ suite('p5.prototype.noise', function () {
+ beforeEach(function () {
result = mockP5Prototype.noise(0);
});
- test('should return a number', function() {
+ test('should return a number', function () {
assert.typeOf(result, 'number');
});
- test('should return a number 0 < n < 1', function() {
+ test('should return a number 0 < n < 1', function () {
assert.isTrue(result > 0);
assert.isTrue(result < 1);
});
});
// Test for noiseSeed
- suite('p5.prototype.noiseSeed', function() {
- beforeEach(function() {
+ suite('p5.prototype.noiseSeed', function () {
+ beforeEach(function () {
mockP5Prototype.noiseSeed(99);
var t = 0;
for (var i = 0; i < 5; i++) {
@@ -50,14 +49,14 @@ suite('Noise', function() {
t += 0.01;
}
});
- test('should return a number 0 < n < 1', function() {
+ test('should return a number 0 < n < 1', function () {
for (var i = 0; i < results.length; i++) {
assert.typeOf(results[i], 'number');
assert.isTrue(results[i] > 0);
assert.isTrue(results[i] < 1);
}
});
- test('should return same sequence of numbers', function() {
+ test('should return same sequence of numbers', function () {
for (var i = 0; i < 5; i++) {
assert.isTrue(results[i] === results[i + 5]);
}
diff --git a/test/unit/math/p5.Matrix.js b/test/unit/math/p5.Matrix.js
index acce52506c..762d7f3779 100644
--- a/test/unit/math/p5.Matrix.js
+++ b/test/unit/math/p5.Matrix.js
@@ -13,7 +13,6 @@ var other = Float32Array.from([
var mat3 = Float32Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9]);
-
suite('p5.Matrix', function () {
var myp5;
@@ -39,7 +38,6 @@ suite('p5.Matrix', function () {
[].slice.call(m.mat4),
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
);
-
});
test('new p5.Matrix(array)', function () {
@@ -64,7 +62,6 @@ suite('p5.Matrix', function () {
expect(toArray(m.mat4)).toEqual([
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
]);
-
});
});
@@ -154,13 +151,11 @@ suite('p5.Matrix', function () {
suite.todo('add', () => {});
suite('mult', function () {
-
var mm = [
30, 70, 110, 150, 70, 174, 278, 382, 110, 278, 446, 614, 150, 382, 614,
846
];
-
test('self', function () {
var m = new p5.Matrix(mat4.slice());
m.mult(m);
@@ -172,7 +167,6 @@ suite('p5.Matrix', function () {
542, 600
]
);
-
});
test('p5.Matrix', function () {
@@ -196,13 +190,11 @@ suite('p5.Matrix', function () {
});
suite('apply', function () {
-
var am = [
276, 304, 332, 360, 304, 336, 368, 400, 332, 368, 404, 440, 360, 400, 440,
480
];
-
test('self', function () {
var m = new p5.Matrix(mat4.slice());
m.apply(m);
@@ -214,7 +206,6 @@ suite('p5.Matrix', function () {
542, 600
]
);
-
});
test('p5.Matrix', function () {
@@ -238,10 +229,8 @@ suite('p5.Matrix', function () {
});
suite('scale', function () {
-
var sm = [2, 4, 6, 8, 15, 18, 21, 24, 45, 50, 55, 60, 13, 14, 15, 16];
-
var mat4 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
test('p5.Vector', function () {
var m = new p5.Matrix(mat4.slice());
@@ -267,7 +256,6 @@ suite('p5.Matrix', function () {
});
suite('rotate', function () {
-
var rm = Float32Array.from([
1.433447866601989, 2.5241247073503885, 3.6148015480987885,
4.7054783888471885, 6.460371405020393, 7.054586073938033,
@@ -275,7 +263,6 @@ suite('p5.Matrix', function () {
9.157598472697025, 10.36479893504708, 11.571999397397136, 13, 14, 15, 16
]);
-
test('p5.Vector', function () {
var m = new p5.Matrix(mat4.slice());
var v = myp5.createVector(2, 3, 5);
@@ -329,19 +316,19 @@ suite('p5.Matrix', function () {
const multMatrix = new p5.Matrix([1, 1, 1, 0, 1, 1, 1, 0, 1]);
// When taking a matrix as an argument
m.mult(multMatrix);
- expect(toArray(m.mat3)).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]);
+ expect(toArray(m.mat3)).toEqual([4, 3, 6, 10, 9, 15, 16, 15, 24]);
});
test('mult a 3x3 matrix with array as argument', function () {
const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
m.mult([1, 1, 1, 0, 1, 1, 1, 0, 1]);
- expect(toArray(m.mat3)).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]);
+ expect(toArray(m.mat3)).toEqual([4, 3, 6, 10, 9, 15, 16, 15, 24]);
});
test('mult a 3x3 matrix with arguments non array', function () {
const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
m.mult(1, 1, 1, 0, 1, 1, 1, 0, 1);
- expect(toArray(m.mat3)).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]);
+ expect(toArray(m.mat3)).toEqual([4, 3, 6, 10, 9, 15, 16, 15, 24]);
});
test('column() and row()', function () {
@@ -435,7 +422,8 @@ suite('p5.Matrix', function () {
expect(mat.mat3).toEqual([1, 4, 7, 2, 5, 8, 3, 6, 9]);
});
});
- describe.skip('Determinant', () => { // TODO: Cristian, when this is public we'll add tests
+ describe.skip('Determinant', () => {
+ // TODO: Cristian, when this is public we'll add tests
it('should calculate the determinant of a 4x4 matrix', () => {
const mat4 = new p5.Matrix([
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
@@ -494,8 +482,9 @@ suite('p5.Matrix', function () {
const matrix = new p5.Matrix([1, 2, 3, 0, 1, 4, 5, 6, 0]);
const invertedMatrix = matrix.invert();
- expect(toArray(invertedMatrix.mat3))
- .toEqual([-24, 18, 5, 20, -15, -4, -5, 4, 1]);
+ expect(toArray(invertedMatrix.mat3)).toEqual([
+ -24, 18, 5, 20, -15, -4, -5, 4, 1
+ ]);
});
it('should return null for a non-invertible 3x3 matrix', () => {
diff --git a/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js
index bbe7c38a5b..f951d6544a 100644
--- a/test/unit/math/p5.Vector.js
+++ b/test/unit/math/p5.Vector.js
@@ -1,15 +1,17 @@
import { default as vector, Vector } from '../../../src/math/p5.Vector.js';
import { default as math } from '../../../src/math/math.js';
-import { _defaultEmptyVector, _validatedVectorOperation } from '../../../src/math/patch-vector.js';
+import {
+ _defaultEmptyVector,
+ _validatedVectorOperation
+} from '../../../src/math/patch-vector.js';
import { vi } from 'vitest';
-
suite('p5.Vector', function () {
var v;
let FESCalled = false;
const mockP5 = {
- _friendlyError: function(msg, func) {
+ _friendlyError: function (msg, func) {
FESCalled = true;
console.warn(msg);
}
@@ -30,11 +32,26 @@ suite('p5.Vector', function () {
);
// The following mocks simulate the validation decorator
- Vector.prototype.add = _validatedVectorOperation(false)(Vector.prototype.add, options);
- Vector.prototype.sub = _validatedVectorOperation(false)(Vector.prototype.sub, options);
- Vector.prototype.mult = _validatedVectorOperation(true)(Vector.prototype.mult, options);
- Vector.prototype.rem = _validatedVectorOperation(true)(Vector.prototype.rem, options);
- Vector.prototype.div = _validatedVectorOperation(true)(Vector.prototype.div, options);
+ Vector.prototype.add = _validatedVectorOperation(false)(
+ Vector.prototype.add,
+ options
+ );
+ Vector.prototype.sub = _validatedVectorOperation(false)(
+ Vector.prototype.sub,
+ options
+ );
+ Vector.prototype.mult = _validatedVectorOperation(true)(
+ Vector.prototype.mult,
+ options
+ );
+ Vector.prototype.rem = _validatedVectorOperation(true)(
+ Vector.prototype.rem,
+ options
+ );
+ Vector.prototype.div = _validatedVectorOperation(true)(
+ Vector.prototype.div,
+ options
+ );
});
afterEach(function () {});
@@ -102,7 +119,6 @@ suite('p5.Vector', function () {
});
});
-
suite.todo('p5.prototype.createVector()', function () {
beforeEach(function () {
v = mockP5Prototype.createVector();
@@ -116,7 +132,7 @@ suite('p5.Vector', function () {
});
test('should have values be initialized to 0,0,0', function () {
- assert.deepEqual(v.values, [0,0,0]);
+ assert.deepEqual(v.values, [0, 0, 0]);
});
test('should have dimensions initialized to 3', function () {
@@ -296,15 +312,12 @@ suite('p5.Vector', function () {
expect(v1.angleBetween(v2)).to.equal(0);
});
- test.todo(
- 'between [0,3,0] and [0,-3,0] should be 180 degrees',
- function () {
- mockP5Prototype.angleMode(DEGREES);
- v1 = new Vector(0, 3, 0);
- v2 = new Vector(0, -3, 0);
- expect(v1.angleBetween(v2)).to.be.closeTo(180, 0.01);
- }
- );
+ test.todo('between [0,3,0] and [0,-3,0] should be 180 degrees', function () {
+ mockP5Prototype.angleMode(DEGREES);
+ v1 = new Vector(0, 3, 0);
+ v2 = new Vector(0, -3, 0);
+ expect(v1.angleBetween(v2)).to.be.closeTo(180, 0.01);
+ });
test('between [1,0,0] and [2,2,0] should be 1/4 PI radians', function () {
v1 = new Vector(1, 0, 0);
@@ -358,15 +371,12 @@ suite('p5.Vector', function () {
expect(Vector.angleBetween(v2, v1)).to.be.NaN;
});
- test.todo(
- 'between [1,0,0] and [0,-1,0] should be -90 degrees',
- function () {
- mockP5Prototype.angleMode(DEGREES);
- v1 = new Vector(1, 0, 0);
- v2 = new Vector(0, -1, 0);
- expect(Vector.angleBetween(v1, v2)).to.be.closeTo(-90, 0.01);
- }
- );
+ test.todo('between [1,0,0] and [0,-1,0] should be -90 degrees', function () {
+ mockP5Prototype.angleMode(DEGREES);
+ v1 = new Vector(1, 0, 0);
+ v2 = new Vector(0, -1, 0);
+ expect(Vector.angleBetween(v1, v2)).to.be.closeTo(-90, 0.01);
+ });
test('between [0,3,0] and [0,-3,0] should be PI radians', function () {
v1 = new Vector(0, 3, 0);
@@ -1018,7 +1028,6 @@ suite('p5.Vector', function () {
});
});
-
suite('smaller dimension', function () {
let v1, v2, v3;
beforeEach(function () {
@@ -1052,10 +1061,10 @@ suite('p5.Vector', function () {
});
test('should be prioritized in div()', function () {
- assert.deepEqual(v1.div(v2).values, [1/2]);
+ assert.deepEqual(v1.div(v2).values, [1 / 2]);
expect(v1.div(v2).dimensions).to.eql(1);
- assert.deepEqual(v3.div(v2).values, [2, 5/3]);
+ assert.deepEqual(v3.div(v2).values, [2, 5 / 3]);
expect(v3.div(v2).dimensions).to.eql(2);
});
@@ -1397,7 +1406,7 @@ suite('p5.Vector', function () {
suite('heading', function () {
beforeEach(function () {
- v = new Vector(0,0,0);
+ v = new Vector(0, 0, 0);
});
suite('p5.Vector.prototype.heading() [INSTANCE]', function () {
@@ -1487,10 +1496,10 @@ suite('p5.Vector', function () {
expect(v.lerp()).to.eql(v);
});
- suite('with p5.Vector', function() {
- test('should call lerp with 4 arguments', function() {
+ suite('with p5.Vector', function () {
+ test('should call lerp with 4 arguments', function () {
vi.spyOn(v, 'lerp');
- v.lerp(new Vector(1,2,3), 1);
+ v.lerp(new Vector(1, 2, 3), 1);
expect(v.lerp).toHaveBeenCalledWith(1, 2, 3, 1);
});
});
@@ -1725,35 +1734,19 @@ suite('p5.Vector', function () {
incoming_x = 1;
incoming_y = 1;
incoming_z = 1;
- original_incoming = new Vector(
- incoming_x,
- incoming_y,
- incoming_z
- );
+ original_incoming = new Vector(incoming_x, incoming_y, incoming_z);
x_normal = new Vector(3, 0, 0);
y_normal = new Vector(0, 3, 0);
z_normal = new Vector(0, 0, 3);
- x_bounce_incoming = new Vector(
- incoming_x,
- incoming_y,
- incoming_z
- );
+ x_bounce_incoming = new Vector(incoming_x, incoming_y, incoming_z);
x_bounce_outgoing = x_bounce_incoming.reflect(x_normal);
- y_bounce_incoming = new Vector(
- incoming_x,
- incoming_y,
- incoming_z
- );
+ y_bounce_incoming = new Vector(incoming_x, incoming_y, incoming_z);
y_bounce_outgoing = y_bounce_incoming.reflect(y_normal);
- z_bounce_incoming = new Vector(
- incoming_x,
- incoming_y,
- incoming_z
- );
+ z_bounce_incoming = new Vector(incoming_x, incoming_y, incoming_z);
z_bounce_outgoing = z_bounce_incoming.reflect(z_normal);
});
@@ -1836,11 +1829,7 @@ suite('p5.Vector', function () {
incoming_x = 1;
incoming_y = 1;
incoming_z = 1;
- original_incoming = new Vector(
- incoming_x,
- incoming_y,
- incoming_z
- );
+ original_incoming = new Vector(incoming_x, incoming_y, incoming_z);
x_target = new Vector(0, 0, 0);
y_target = new Vector(0, 0, 0);
z_target = new Vector(0, 0, 0);
@@ -1849,33 +1838,21 @@ suite('p5.Vector', function () {
y_normal = new Vector(0, 3, 0);
z_normal = new Vector(0, 0, 3);
- x_bounce_incoming = new Vector(
- incoming_x,
- incoming_y,
- incoming_z
- );
+ x_bounce_incoming = new Vector(incoming_x, incoming_y, incoming_z);
x_bounce_outgoing = Vector.reflect(
x_bounce_incoming,
x_normal,
x_target
);
- y_bounce_incoming = new Vector(
- incoming_x,
- incoming_y,
- incoming_z
- );
+ y_bounce_incoming = new Vector(incoming_x, incoming_y, incoming_z);
y_bounce_outgoing = Vector.reflect(
y_bounce_incoming,
y_normal,
y_target
);
- z_bounce_incoming = new Vector(
- incoming_x,
- incoming_y,
- incoming_z
- );
+ z_bounce_incoming = new Vector(incoming_x, incoming_y, incoming_z);
z_bounce_outgoing = Vector.reflect(
z_bounce_incoming,
z_normal,
@@ -2120,14 +2097,12 @@ suite('p5.Vector', function () {
assert.equal(vect.getValue(3), 4);
});
- test('should throw friendly error if attempting to get element outside length',
- function () {
- let vect = new Vector(1, 2, 3, 4);
- FESCalled = false;
- assert.equal(vect.getValue(5), undefined);
- assert.equal(FESCalled, true);
- }
- );
+ test('should throw friendly error if attempting to get element outside length', function () {
+ let vect = new Vector(1, 2, 3, 4);
+ FESCalled = false;
+ assert.equal(vect.getValue(5), undefined);
+ assert.equal(FESCalled, true);
+ });
});
suite('set value', function () {
@@ -2140,14 +2115,12 @@ suite('p5.Vector', function () {
assert.equal(vect.getValue(3), 4);
});
- test('should throw friendly error if attempting to set element outside lenght',
- function () {
- let vect = new Vector(1, 2, 3, 4);
- FESCalled = false;
- vect.setValue(100, 7);
- assert.equal(FESCalled, true);
- }
- );
+ test('should throw friendly error if attempting to set element outside lenght', function () {
+ let vect = new Vector(1, 2, 3, 4);
+ FESCalled = false;
+ vect.setValue(100, 7);
+ assert.equal(FESCalled, true);
+ });
});
describe('get w', () => {
diff --git a/test/unit/math/random.js b/test/unit/math/random.js
index 0105b5ade8..39494985da 100644
--- a/test/unit/math/random.js
+++ b/test/unit/math/random.js
@@ -1,24 +1,23 @@
import random from '../../../src/math/random.js';
import { vi } from 'vitest';
-suite('Random', function() {
+suite('Random', function () {
const mockP5 = {
_validateParameters: vi.fn()
};
- const mockP5Prototype = {
- };
+ const mockP5Prototype = {};
- beforeAll(function() {
+ beforeAll(function () {
random(mockP5, mockP5Prototype);
});
- suite('p5.prototype.random', function() {
+ suite('p5.prototype.random', function () {
var result;
var results = [];
- suite('random()', function() {
- beforeEach(function() {
+ suite('random()', function () {
+ beforeEach(function () {
mockP5Prototype.randomSeed(99);
for (var i = 0; i < 5; i++) {
results[i] = mockP5Prototype.random();
@@ -28,77 +27,77 @@ suite('Random', function() {
results[i] = mockP5Prototype.random();
}
});
- test('should return a number', function() {
+ test('should return a number', function () {
for (var i = 0; i < 10; i++) {
assert.typeOf(results[i], 'number');
}
});
- test('should return a number 0 <= n < 1', function() {
+ test('should return a number 0 <= n < 1', function () {
for (var i = 0; i < 10; i++) {
assert.isTrue(results[i] >= 0);
assert.isTrue(results[i] < 1);
}
});
- test('should return same sequence of numbers', function() {
+ test('should return same sequence of numbers', function () {
for (var i = 0; i < 5; i++) {
assert.isTrue(results[i] === results[i + 5]);
}
});
});
- suite('random(5)', function() {
- test('should return a number 0 <= n < 5', function() {
+ suite('random(5)', function () {
+ test('should return a number 0 <= n < 5', function () {
result = mockP5Prototype.random(5);
assert.isTrue(result >= 0);
assert.isTrue(result < 5);
});
});
- suite('random(1, 10)', function() {
- test('should return a number 1 <= n < 10', function() {
+ suite('random(1, 10)', function () {
+ test('should return a number 1 <= n < 10', function () {
result = mockP5Prototype.random(1, 10);
assert.isTrue(result >= 1);
assert.isTrue(result < 10);
});
});
- suite('random(["apple", "pear", "orange", "grape"])', function() {
- test('should return a fruit', function() {
+ suite('random(["apple", "pear", "orange", "grape"])', function () {
+ test('should return a fruit', function () {
var fruits = ['apple', 'pear', 'orange', 'grape'];
result = mockP5Prototype.random(fruits);
assert.include(fruits, result);
});
});
});
- suite.skip('instance mode', function() {
+ suite.skip('instance mode', function () {
var instances = [];
- beforeEach(async function() {
+ beforeEach(async function () {
var instanceCount = 2;
for (var i = 0; i < instanceCount; i++) {
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
instances.push(p);
};
});
}
});
- afterEach(function() {
- instances.forEach(function(instance) {
+ afterEach(function () {
+ instances.forEach(function (instance) {
instance.remove();
});
});
- test('should be independent', function() {
+ test('should be independent', function () {
var SEED = 42;
- instances.forEach(function(instance) {
+ instances.forEach(function (instance) {
instance.randomSeed(SEED);
});
for (var i = 0; i < 10; i++) {
- instances.reduce(function(prev, instance) {
+ instances.reduce(function (prev, instance) {
var randomValue = instance.random();
if (prev != null) {
assert.equal(randomValue, prev);
@@ -110,36 +109,36 @@ suite('Random', function() {
});
});
- suite('p5.prototype.randomGaussian', function() {
- suite.skip('instance mode', function() {
+ suite('p5.prototype.randomGaussian', function () {
+ suite.skip('instance mode', function () {
var instances = [];
- beforeEach(async function() {
+ beforeEach(async function () {
var instanceCount = 2;
for (var i = 0; i < instanceCount; i++) {
- new p5(function(p) {
- p.setup = function() {
+ new p5(function (p) {
+ p.setup = function () {
instances.push(p);
};
});
}
});
- afterEach(function() {
- instances.forEach(function(instance) {
+ afterEach(function () {
+ instances.forEach(function (instance) {
instance.remove();
});
});
- test('should be independent', function() {
+ test('should be independent', function () {
var SEED = 42;
- instances.forEach(function(instance) {
+ instances.forEach(function (instance) {
instance.randomSeed(SEED);
});
for (var i = 0; i < 10; i++) {
- instances.reduce(function(prev, instance) {
+ instances.reduce(function (prev, instance) {
var randomValue = instance.randomGaussian(0, 15);
if (prev != null) {
assert.equal(randomValue, prev);
@@ -151,8 +150,8 @@ suite('Random', function() {
});
});
- suite('randomGaussian(42, 0)', function() {
- test('should return 42', function() {
+ suite('randomGaussian(42, 0)', function () {
+ test('should return 42', function () {
let result = mockP5Prototype.randomGaussian(42, 0);
assert.isTrue(result === 42);
});
diff --git a/test/unit/math/trigonometry.js b/test/unit/math/trigonometry.js
index 62b8bbd532..02c6ed3762 100644
--- a/test/unit/math/trigonometry.js
+++ b/test/unit/math/trigonometry.js
@@ -1,140 +1,140 @@
import trigonometry from '../../../src/math/trigonometry.js';
import { vi } from 'vitest';
-suite('Trigonometry', function() {
+suite('Trigonometry', function () {
let theta = 90;
let x = 0;
let y = 1;
let ratio = 0.5;
- const mockP5Prototype = {
- };
+ const mockP5Prototype = {};
const mockP5 = Object.create(mockP5Prototype, {
_validateParameters: vi.fn()
});
- beforeEach(async function() {
+ beforeEach(async function () {
trigonometry(mockP5, mockP5Prototype);
});
- afterAll(function() {
- });
+ afterAll(function () {});
- var handleDegreesAndRadians = function(func) {
- test('should handle degrees', function() {
+ var handleDegreesAndRadians = function (func) {
+ test('should handle degrees', function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
var degToRad = mockP5Prototype.radians(theta);
assert.equal(Math[func](degToRad), mockP5Prototype[func](theta));
});
- test('should handle radians', function() {
+ test('should handle radians', function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
assert.equal(Math[func](theta), mockP5Prototype[func](theta));
});
};
- var ahandleDegreesAndRadians = function(func) {
- test('should handle degrees', function() {
+ var ahandleDegreesAndRadians = function (func) {
+ test('should handle degrees', function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
assert.equal(
- mockP5Prototype.degrees(Math[func](ratio)), mockP5Prototype[func](ratio)
+ mockP5Prototype.degrees(Math[func](ratio)),
+ mockP5Prototype[func](ratio)
);
});
- test('should handle radians', function() {
+ test('should handle radians', function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
assert.equal(Math[func](ratio), mockP5Prototype[func](ratio));
});
};
- suite('p5.prototype.angleMode', function() {
- test('should set constant to DEGREES', function() {
+ suite('p5.prototype.angleMode', function () {
+ test('should set constant to DEGREES', function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
assert.equal(mockP5Prototype.angleMode(), mockP5.DEGREES);
});
- test('should set constant to RADIANS', function() {
+ test('should set constant to RADIANS', function () {
mockP5Prototype.angleMode(mockP5Prototype.RADIANS);
assert.equal(mockP5Prototype.angleMode(), mockP5.RADIANS);
});
- test('should return radians', function() {
+ test('should return radians', function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
assert.equal(mockP5Prototype.angleMode(), mockP5.RADIANS);
});
- test('should return degrees', function() {
+ test('should return degrees', function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
assert.equal(mockP5Prototype.angleMode(), mockP5.DEGREES);
});
- test('should always be RADIANS or DEGREES', function() {
+ test('should always be RADIANS or DEGREES', function () {
mockP5Prototype.angleMode('wtflolzkk');
assert.equal(mockP5Prototype.angleMode(), mockP5.RADIANS);
});
});
- suite('p5.prototype.degrees', function() {
- test('should return the angle in radians when angleMode is DEGREES', function() {
+ suite('p5.prototype.degrees', function () {
+ test('should return the angle in radians when angleMode is DEGREES', function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
- var angleInRad = 360 * theta / (2 * Math.PI); // This is degToRad conversion
+ var angleInRad = (360 * theta) / (2 * Math.PI); // This is degToRad conversion
assert.equal(mockP5Prototype.degrees(theta), angleInRad);
});
- test('should return the angle in radians when angleMode is RADIANS', function() {
+ test('should return the angle in radians when angleMode is RADIANS', function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
- var angleInRad = 360 * theta / (2 * Math.PI); // This is degToRad conversion
+ var angleInRad = (360 * theta) / (2 * Math.PI); // This is degToRad conversion
assert.equal(mockP5Prototype.degrees(theta), angleInRad);
});
});
- suite('p5.prototype.radians', function() {
- test('should return the angle in degrees when angleMode is RADIANS', function() {
+ suite('p5.prototype.radians', function () {
+ test('should return the angle in degrees when angleMode is RADIANS', function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
- var angleInDeg = 2 * Math.PI * theta / 360; // This is RadToDeg conversion
+ var angleInDeg = (2 * Math.PI * theta) / 360; // This is RadToDeg conversion
assert.equal(mockP5Prototype.radians(theta), angleInDeg);
});
- test('should return the angle in degrees when angleMode is DEGREES', function() {
+ test('should return the angle in degrees when angleMode is DEGREES', function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
- var angleInDeg = 2 * Math.PI * theta / 360; // This is RadToDeg conversion
+ var angleInDeg = (2 * Math.PI * theta) / 360; // This is RadToDeg conversion
assert.equal(mockP5Prototype.radians(theta), angleInDeg);
});
});
- suite('p5.prototype.asin', function() {
+ suite('p5.prototype.asin', function () {
ahandleDegreesAndRadians('asin');
});
- suite('p5.prototype.atan', function() {
+ suite('p5.prototype.atan', function () {
ahandleDegreesAndRadians('atan');
});
- suite('p5.prototype.acos', function() {
+ suite('p5.prototype.acos', function () {
ahandleDegreesAndRadians('acos');
});
- suite('p5.prototype.sin', function() {
+ suite('p5.prototype.sin', function () {
handleDegreesAndRadians('sin');
});
- suite('p5.prototype.cos', function() {
+ suite('p5.prototype.cos', function () {
handleDegreesAndRadians('cos');
});
- suite('p5.prototype.tan', function() {
+ suite('p5.prototype.tan', function () {
handleDegreesAndRadians('tan');
});
- suite('p5.prototype.atan2', function() {
- test('should handle degrees', function() {
+ suite('p5.prototype.atan2', function () {
+ test('should handle degrees', function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
assert.equal(
- mockP5Prototype.degrees(Math.atan2(y, x)), mockP5Prototype.atan2(y, x)
+ mockP5Prototype.degrees(Math.atan2(y, x)),
+ mockP5Prototype.atan2(y, x)
);
});
- test('should handle radians', function() {
+ test('should handle radians', function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
assert.equal(Math.atan2(y, x), mockP5Prototype.atan2(y, x));
});
diff --git a/test/unit/spec.js b/test/unit/spec.js
index 7dd87068a7..fd9bbc759f 100644
--- a/test/unit/spec.js
+++ b/test/unit/spec.js
@@ -55,7 +55,7 @@ var spec = {
// to omit some for speed if they should only be run manually.
'webgl',
'typography',
- 'shape_modes',
+ 'shape_modes'
]
};
document.write(
diff --git a/test/unit/type/attributes.js b/test/unit/type/attributes.js
index c6b1f26697..0315deac40 100644
--- a/test/unit/type/attributes.js
+++ b/test/unit/type/attributes.js
@@ -1,12 +1,12 @@
import p5 from '../../../src/app.js';
-suite('Typography Attributes', function() {
+suite('Typography Attributes', function () {
var myp5;
beforeEach(function () {
myp5 = new p5(function (p) {
- p.setup = function () { };
- p.draw = function () { };
+ p.setup = function () {};
+ p.draw = function () {};
});
});
@@ -14,68 +14,70 @@ suite('Typography Attributes', function() {
myp5.remove();
});
- suite('textLeading', function() {
- test('sets and gets the leading value', function() {
+ suite('textLeading', function () {
+ test('sets and gets the leading value', function () {
myp5.textLeading(20);
assert.strictEqual(myp5.textLeading(), 20);
});
- test('should work for negative leadings', function() {
+ test('should work for negative leadings', function () {
myp5.textLeading(-20);
assert.strictEqual(myp5.textLeading(), -20);
});
});
- suite('textSize', function() {
- test('sets and gets the text size', function() {
+ suite('textSize', function () {
+ test('sets and gets the text size', function () {
myp5.textSize(24);
assert.strictEqual(myp5.textSize(), 24);
});
});
- suite('textStyle', function() {
- test('sets and gets the text style', function() {
+ suite('textStyle', function () {
+ test('sets and gets the text style', function () {
myp5.textStyle(myp5.ITALIC);
assert.strictEqual(myp5.textStyle(), myp5.ITALIC);
});
});
- suite('textWidth', function() {
- test('should return a number for char input', function() {
+ suite('textWidth', function () {
+ test('should return a number for char input', function () {
assert.isNumber(myp5.textWidth('P'));
});
test('should return a number for string input.', function () {
assert.isNumber(myp5.textWidth('p5.js'));
});
// Either should not throw error
- test('should return a number for number input', function() {
+ test('should return a number for number input', function () {
assert.isNumber(myp5.textWidth(100));
});
- test('should ignore leading and trailing spaces', function() {
+ test('should ignore leading and trailing spaces', function () {
assert.strictEqual(myp5.textWidth(' Hello '), myp5.textWidth('Hello'));
- assert.strictEqual(myp5.textWidth(' Hello '), myp5.textWidth('Hello'));
+ assert.strictEqual(
+ myp5.textWidth(' Hello '),
+ myp5.textWidth('Hello')
+ );
});
});
- suite('textAscent', function() {
- test('should return a number', function() {
+ suite('textAscent', function () {
+ test('should return a number', function () {
assert.isNumber(myp5.textAscent());
});
});
- suite('textDescent', function() {
- test('should return a number', function() {
+ suite('textDescent', function () {
+ test('should return a number', function () {
assert.isNumber(myp5.textDescent());
});
});
- suite('textWrap', function() {
- test('gets the default text wrap attribute', function() {
+ suite('textWrap', function () {
+ test('gets the default text wrap attribute', function () {
assert.strictEqual(myp5.textWrap(), myp5.WORD);
});
- test('sets and gets the text wrap value', function() {
+ test('sets and gets the text wrap value', function () {
myp5.textWrap(myp5.CHAR);
assert.strictEqual(myp5.textWrap(), myp5.CHAR);
});
-
});
});
diff --git a/test/unit/type/loading.js b/test/unit/type/loading.js
index 8d90d6fb40..60483be761 100644
--- a/test/unit/type/loading.js
+++ b/test/unit/type/loading.js
@@ -5,8 +5,8 @@ suite('Loading Fonts', function () {
beforeEach(function () {
myp5 = new p5(function (p) {
- p.setup = function () { };
- p.draw = function () { };
+ p.setup = function () {};
+ p.draw = function () {};
});
});
@@ -18,7 +18,9 @@ suite('Loading Fonts', function () {
const fontFile = 'test/unit/assets/acmesa.ttf';
test('loadFont on zlib compressed fonts works', async () => {
- const font = await myp5.loadFont('https://fonts.gstatic.com/s/montserrat/v29/JTUFjIg1_i6t8kCHKm459Wx7xQYXK0vOoz6jq6R8aXw.woff');
+ const font = await myp5.loadFont(
+ 'https://fonts.gstatic.com/s/montserrat/v29/JTUFjIg1_i6t8kCHKm459Wx7xQYXK0vOoz6jq6R8aXw.woff'
+ );
expect(font.data).toBeTruthy();
});
@@ -29,24 +31,23 @@ suite('Loading Fonts', function () {
assert.isTrue(pFont instanceof p5.Font);
});
- test('loadFont.then', async () => new Promise(done => {
-
- myp5.loadFont(fontFile, 'acmesa').then(pFont => {
- assert.ok(pFont, 'acmesa.ttf loaded');
- assert.equal(pFont.name, 'acmesa');
- assert.isTrue(pFont instanceof p5.Font);
- done();
- });
-
- }));
-
- test.skip('loadFont.callback', async () => new Promise(done => {
- myp5.loadFont(fontFile, pFont => {
- assert.ok(pFont, 'acmesa.ttf loaded');
- assert.equal(pFont.name, 'A.C.M.E. Secret Agent');
- assert.isTrue(pFont instanceof p5.Font);
- done();
- });
- }));
-
+ test('loadFont.then', async () =>
+ new Promise(done => {
+ myp5.loadFont(fontFile, 'acmesa').then(pFont => {
+ assert.ok(pFont, 'acmesa.ttf loaded');
+ assert.equal(pFont.name, 'acmesa');
+ assert.isTrue(pFont instanceof p5.Font);
+ done();
+ });
+ }));
+
+ test.skip('loadFont.callback', async () =>
+ new Promise(done => {
+ myp5.loadFont(fontFile, pFont => {
+ assert.ok(pFont, 'acmesa.ttf loaded');
+ assert.equal(pFont.name, 'A.C.M.E. Secret Agent');
+ assert.isTrue(pFont instanceof p5.Font);
+ done();
+ });
+ }));
});
diff --git a/test/unit/type/p5.Font.js b/test/unit/type/p5.Font.js
index ecefcab6bd..1d2daf4bd4 100644
--- a/test/unit/type/p5.Font.js
+++ b/test/unit/type/p5.Font.js
@@ -1,13 +1,13 @@
import p5 from '../../../src/app.js';
-import {_sanitizeFontName} from '../../../src/type/p5.Font.js';
+import { _sanitizeFontName } from '../../../src/type/p5.Font.js';
suite('p5.Font', function () {
var myp5;
beforeEach(function () {
myp5 = new p5(function (p) {
- p.setup = function () { };
- p.draw = function () { };
+ p.setup = function () {};
+ p.draw = function () {};
});
});
@@ -41,14 +41,14 @@ suite('p5.Font', function () {
assert.property(bbox, 'h');
});
- test('fontBounds no NaN (multiline + CENTER)', async () => {
- const pFont = await myp5.loadFont(fontFile);
- myp5.textAlign(myp5.CENTER, myp5.CENTER);
- const b = pFont.fontBounds('Hello,\nWorld!', 50, 50, 24);
- expect(b.x).not.toBeNaN();
- expect(b.y).not.toBeNaN();
- expect(b.w).not.toBeNaN();
- expect(b.h).not.toBeNaN();
+ test('fontBounds no NaN (multiline + CENTER)', async () => {
+ const pFont = await myp5.loadFont(fontFile);
+ myp5.textAlign(myp5.CENTER, myp5.CENTER);
+ const b = pFont.fontBounds('Hello,\nWorld!', 50, 50, 24);
+ expect(b.x).not.toBeNaN();
+ expect(b.y).not.toBeNaN();
+ expect(b.w).not.toBeNaN();
+ expect(b.h).not.toBeNaN();
});
suite('textToPoints', () => {
@@ -65,7 +65,9 @@ suite('p5.Font', function () {
const font = await myp5.loadFont(fontFile);
myp5.textSize(50);
const pts = font.textToPoints('T', 0, 0);
- const simplifiedPts = font.textToPoints('T', 0, 0, { simplifyThreshold: Math.PI * 0.01 });
+ const simplifiedPts = font.textToPoints('T', 0, 0, {
+ simplifyThreshold: Math.PI * 0.01
+ });
expect(pts.length).toBeGreaterThan(simplifiedPts.length);
});
});
diff --git a/test/unit/utilities/conversion.js b/test/unit/utilities/conversion.js
index a4a217cb9e..0a529b04e8 100644
--- a/test/unit/utilities/conversion.js
+++ b/test/unit/utilities/conversion.js
@@ -1,317 +1,335 @@
import { mockP5, mockP5Prototype } from '../../js/mocks';
import conversion from '../../../src/utilities/conversion';
-suite('Conversion', function() {
- beforeAll(function() {
+suite('Conversion', function () {
+ beforeAll(function () {
conversion(mockP5, mockP5Prototype);
});
- suite('p5.prototype.float', function() {
- test('should be a function', function() {
+ suite('p5.prototype.float', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.float);
assert.typeOf(mockP5Prototype.float, 'function');
});
- test('should convert a string to its floating point representation', function() {
+ test('should convert a string to its floating point representation', function () {
const result = mockP5Prototype.float('56.99998');
assert.typeOf(result, 'Number');
assert.strictEqual(result, 56.99998);
});
- test('should return NaN for invalid string', function() {
+ test('should return NaN for invalid string', function () {
const result = mockP5Prototype.float('cat');
assert.isNaN(result);
});
- test('should return Infinity for Infinity', function() {
+ test('should return Infinity for Infinity', function () {
const result = mockP5Prototype.float(Infinity);
assert.strictEqual(result, Infinity);
});
- test('should return -Infinity for -Infinity', function() {
+ test('should return -Infinity for -Infinity', function () {
const result = mockP5Prototype.float(-Infinity);
assert.strictEqual(result, -Infinity);
});
- test('should return array of floating points and Nan', function() {
+ test('should return array of floating points and Nan', function () {
const result = mockP5Prototype.float(['1', '2.0', '3.1', 'giraffe']);
assert.typeOf(result, 'Array');
assert.deepEqual(result, [1, 2.0, 3.1, NaN]);
});
});
- suite('p5.prototype.int', function() {
- test('should be a function', function() {
+ suite('p5.prototype.int', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.int);
assert.typeOf(mockP5Prototype.int, 'function');
});
- test('should convert false to its integer representation i.e. 0', function() {
+ test('should convert false to its integer representation i.e. 0', function () {
const result = mockP5Prototype.int(false);
assert.typeOf(result, 'Number');
assert.strictEqual(result, 0);
});
- test('should convert true to its integer representation i.e. 1', function() {
+ test('should convert true to its integer representation i.e. 1', function () {
const result = mockP5Prototype.int(true);
assert.strictEqual(result, 1);
});
- test('should convert a string to its integer representation', function() {
+ test('should convert a string to its integer representation', function () {
const result = mockP5Prototype.int('1001');
assert.strictEqual(result, 1001);
});
- test('should return NaN for invalid string', function() {
+ test('should return NaN for invalid string', function () {
const result = mockP5Prototype.int('cat');
assert.isNaN(result);
});
- test('should return Infinity for Infinity', function() {
+ test('should return Infinity for Infinity', function () {
const result = mockP5Prototype.int(Infinity);
assert.strictEqual(result, Infinity);
});
- test('should return -Infinity for -Infinity', function() {
+ test('should return -Infinity for -Infinity', function () {
const result = mockP5Prototype.int(-Infinity);
assert.strictEqual(result, -Infinity);
});
- test('should convert float to its integer representation', function() {
+ test('should convert float to its integer representation', function () {
const result = mockP5Prototype.int('-1001.9');
assert.strictEqual(result, -1001);
});
- test('should return array of integers and NaN', function() {
- const result = mockP5Prototype.int(['1', '2.3', '-3.5', 'giraffe', false, 4.7]);
+ test('should return array of integers and NaN', function () {
+ const result = mockP5Prototype.int([
+ '1',
+ '2.3',
+ '-3.5',
+ 'giraffe',
+ false,
+ 4.7
+ ]);
assert.typeOf(result, 'Array');
assert.deepEqual(result, [1, 2, -3, NaN, 0, 4]);
});
});
- suite('p5.prototype.str', function() {
- test('should be a function', function() {
+ suite('p5.prototype.str', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.str);
assert.typeOf(mockP5Prototype.str, 'function');
});
- test('should convert false to string', function() {
+ test('should convert false to string', function () {
const result = mockP5Prototype.str(false);
assert.typeOf(result, 'String');
assert.strictEqual(result, 'false');
});
- test('should convert true to string', function() {
+ test('should convert true to string', function () {
const result = mockP5Prototype.str(true);
assert.strictEqual(result, 'true');
});
- test('should convert a number to string', function() {
+ test('should convert a number to string', function () {
const result = mockP5Prototype.str(45);
assert.strictEqual(result, '45');
});
- test('should return array of strings', function() {
+ test('should return array of strings', function () {
const result = mockP5Prototype.str([1, 2.3, true, -4.5]);
assert.typeOf(result, 'Array');
assert.deepEqual(result, ['1', '2.3', 'true', '-4.5']);
});
});
- suite('p5.prototype.boolean', function() {
- test('should be a function', function() {
+ suite('p5.prototype.boolean', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.boolean);
assert.typeOf(mockP5Prototype.boolean, 'function');
});
- test('should convert 1 to true', function() {
+ test('should convert 1 to true', function () {
const result = mockP5Prototype.boolean(1);
assert.strictEqual(result, true);
});
- test('should convert a number to true', function() {
+ test('should convert a number to true', function () {
const result = mockP5Prototype.boolean(154);
assert.strictEqual(result, true);
});
- test('should return true for Infinity', function() {
+ test('should return true for Infinity', function () {
const result = mockP5Prototype.boolean(Infinity);
assert.strictEqual(result, true);
});
- test('should convert 0 to false', function() {
+ test('should convert 0 to false', function () {
const result = mockP5Prototype.boolean(0);
assert.strictEqual(result, false);
});
- test('should convert a string to false', function() {
+ test('should convert a string to false', function () {
const result = mockP5Prototype.boolean('1');
assert.strictEqual(result, false);
});
- test('should convert a string to false', function() {
+ test('should convert a string to false', function () {
const result = mockP5Prototype.boolean('0');
assert.strictEqual(result, false);
});
- test('should convert "true" to true', function() {
+ test('should convert "true" to true', function () {
const result = mockP5Prototype.boolean('true');
assert.strictEqual(result, true);
});
- test('should return false for empty string', function() {
+ test('should return false for empty string', function () {
const result = mockP5Prototype.boolean('');
assert.strictEqual(result, false);
});
- test('should return array of boolean', function() {
- const result = mockP5Prototype.boolean([1, true, -4.5, Infinity, 'cat', '23']);
+ test('should return array of boolean', function () {
+ const result = mockP5Prototype.boolean([
+ 1,
+ true,
+ -4.5,
+ Infinity,
+ 'cat',
+ '23'
+ ]);
assert.typeOf(result, 'Array');
assert.deepEqual(result, [true, true, true, true, false, false]);
});
});
- suite('p5.prototype.byte', function() {
- test('should be a function', function() {
+ suite('p5.prototype.byte', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.byte);
assert.typeOf(mockP5Prototype.byte, 'function');
});
- test('should return 127 for 127', function() {
+ test('should return 127 for 127', function () {
const result = mockP5Prototype.byte(127);
assert.strictEqual(result, 127);
});
- test('should return -128 for 128', function() {
+ test('should return -128 for 128', function () {
const result = mockP5Prototype.byte(128);
assert.strictEqual(result, -128);
});
- test('should return 23 for 23.4', function() {
+ test('should return 23 for 23.4', function () {
const result = mockP5Prototype.byte(23.4);
assert.strictEqual(result, 23);
});
- test('should return 1 for true', function() {
+ test('should return 1 for true', function () {
const result = mockP5Prototype.byte(true);
assert.strictEqual(result, 1);
});
- test('should return 23 for "23.4"', function() {
+ test('should return 23 for "23.4"', function () {
const result = mockP5Prototype.byte('23.4');
assert.strictEqual(result, 23);
});
- test('should return NaN for invalid string', function() {
+ test('should return NaN for invalid string', function () {
const result = mockP5Prototype.byte('cat');
assert.isNaN(result);
});
- test('should return array', function() {
+ test('should return array', function () {
const result = mockP5Prototype.byte([0, 255, '100']);
assert.typeOf(result, 'Array');
assert.deepEqual(result, [0, -1, 100]);
});
});
- suite('p5.prototype.char', function() {
- test('should be a function', function() {
+ suite('p5.prototype.char', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.char);
assert.typeOf(mockP5Prototype.char, 'function');
});
- test('should return the char representation of the number', function() {
+ test('should return the char representation of the number', function () {
const result = mockP5Prototype.char(65);
assert.typeOf(result, 'String');
assert.strictEqual(result, 'A');
});
- test('should return the char representation of the string', function() {
+ test('should return the char representation of the string', function () {
const result = mockP5Prototype.char('65');
assert.strictEqual(result, 'A');
});
- test('should return array', function() {
+ test('should return array', function () {
const result = mockP5Prototype.char([65, 66, '67']);
assert.typeOf(result, 'Array');
assert.deepEqual(result, ['A', 'B', 'C']);
});
});
- suite('p5.prototype.unchar', function() {
- test('should be a function', function() {
+ suite('p5.prototype.unchar', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.unchar);
assert.typeOf(mockP5Prototype.unchar, 'function');
});
- test('should return the integer representation of char', function() {
+ test('should return the integer representation of char', function () {
const result = mockP5Prototype.unchar('A');
assert.typeOf(result, 'Number');
assert.strictEqual(result, 65);
});
- test('should return array of numbers', function() {
+ test('should return array of numbers', function () {
const result = mockP5Prototype.unchar(['A', 'B', 'C']);
assert.typeOf(result, 'Array');
assert.deepEqual(result, [65, 66, 67]);
});
});
- suite('p5.prototype.hex', function() {
- test('should be a function', function() {
+ suite('p5.prototype.hex', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.hex);
assert.typeOf(mockP5Prototype.hex, 'function');
});
- test('should return the hex representation of the number', function() {
+ test('should return the hex representation of the number', function () {
const result = mockP5Prototype.hex(65);
assert.typeOf(result, 'String');
assert.strictEqual(result, '00000041');
});
- test('should return FFFFFFFF for Infinity', function() {
+ test('should return FFFFFFFF for Infinity', function () {
const result = mockP5Prototype.hex(Infinity);
assert.typeOf(result, 'String');
assert.strictEqual(result, 'FFFFFFFF');
});
- test('should return 00000000 for -Infinity', function() {
+ test('should return 00000000 for -Infinity', function () {
const result = mockP5Prototype.hex(-Infinity);
assert.typeOf(result, 'String');
assert.strictEqual(result, '00000000');
});
- test('should return array', function() {
+ test('should return array', function () {
const result = mockP5Prototype.hex([65, 66, 67]);
assert.typeOf(result, 'Array');
assert.deepEqual(result, ['00000041', '00000042', '00000043']);
});
});
- suite('p5.prototype.unhex', function() {
- test('should be a function', function() {
+ suite('p5.prototype.unhex', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.unhex);
assert.typeOf(mockP5Prototype.unchar, 'function');
});
- test('should return the integer representation of hex', function() {
+ test('should return the integer representation of hex', function () {
const result = mockP5Prototype.unhex('00000041');
assert.typeOf(result, 'Number');
assert.strictEqual(result, 65);
});
- test('should return the NaN for empty string', function() {
+ test('should return the NaN for empty string', function () {
const result = mockP5Prototype.unhex('');
assert.isNaN(result);
});
- test('should return the NaN for invalid hex string', function() {
+ test('should return the NaN for invalid hex string', function () {
const result = mockP5Prototype.unhex('lorem');
assert.isNaN(result);
});
- test('should return array of numbers', function() {
- const result = mockP5Prototype.unhex(['00000041', '00000042', '00000043']);
+ test('should return array of numbers', function () {
+ const result = mockP5Prototype.unhex([
+ '00000041',
+ '00000042',
+ '00000043'
+ ]);
assert.typeOf(result, 'Array');
assert.deepEqual(result, [65, 66, 67]);
});
diff --git a/test/unit/utilities/time_date.js b/test/unit/utilities/time_date.js
index 189d7c8e68..777dff1736 100644
--- a/test/unit/utilities/time_date.js
+++ b/test/unit/utilities/time_date.js
@@ -1,83 +1,83 @@
import { mockP5, mockP5Prototype } from '../../js/mocks';
import timeDate from '../../../src/utilities/time_date';
-suite('time and date', function() {
- beforeAll(function() {
+suite('time and date', function () {
+ beforeAll(function () {
timeDate(mockP5, mockP5Prototype);
});
- suite('p5.prototype.year', function() {
- test('should be a function', function() {
+ suite('p5.prototype.year', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.year);
assert.typeOf(mockP5Prototype.year, 'function');
});
- test('should return this year', function() {
+ test('should return this year', function () {
const result = mockP5Prototype.year();
var jsYear = new Date().getFullYear();
assert.equal(result, jsYear);
});
});
- suite('p5.prototype.day', function() {
- test('should be a function', function() {
+ suite('p5.prototype.day', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.day);
assert.typeOf(mockP5Prototype.day, 'function');
});
- test('should return todays day', function() {
+ test('should return todays day', function () {
var jsDay = new Date().getDate();
const result = mockP5Prototype.day();
assert.equal(result, jsDay);
});
});
- suite('p5.prototype.month', function() {
- test('should be a function', function() {
+ suite('p5.prototype.month', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.month);
assert.typeOf(mockP5Prototype.month, 'function');
});
- test("should return today's month", function() {
+ test("should return today's month", function () {
const result = mockP5Prototype.month();
var jsMonth = new Date().getMonth() + 1;
assert.equal(result, jsMonth);
});
});
- suite('p5.prototype.hour', function() {
- test('should be a function', function() {
+ suite('p5.prototype.hour', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.hour);
assert.typeOf(mockP5Prototype.hour, 'function');
});
- test('should return this hour', function() {
+ test('should return this hour', function () {
var jsHour = new Date().getHours();
const result = mockP5Prototype.hour();
assert.equal(result, jsHour);
});
});
- suite('p5.prototype.second', function() {
- test('should be a function', function() {
+ suite('p5.prototype.second', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.second);
assert.typeOf(mockP5Prototype.second, 'function');
});
- test('should return this second', function() {
+ test('should return this second', function () {
var jsSecond = new Date().getSeconds();
const result = mockP5Prototype.second();
assert.equal(result, jsSecond); //(Math.abs(jsSecond - result), '==', 0, 'everything is ok'); // in my testing, found this might be off by 1 second
});
});
- suite('p5.prototype.minute', function() {
- test('should be a function', function() {
+ suite('p5.prototype.minute', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.minute);
assert.typeOf(mockP5Prototype.minute, 'function');
});
- test('should return a number that is this minute', function() {
+ test('should return a number that is this minute', function () {
var jsMinute = new Date().getMinutes();
const result = mockP5Prototype.minute();
assert.isNumber(result);
@@ -86,28 +86,33 @@ suite('time and date', function() {
});
});
- suite('p5.prototype.millis', function() {
- test('should be a function', function() {
+ suite('p5.prototype.millis', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.millis);
assert.typeOf(mockP5Prototype.millis, 'function');
});
- test('result should be a number', function() {
+ test('result should be a number', function () {
assert.isNumber(mockP5Prototype.millis());
});
// TODO: need to move internal state to module
- test.todo('result should be greater than running time', function() {
+ test.todo('result should be greater than running time', function () {
var runningTime = 50;
var init_date = window.performance.now();
// wait :\
while (window.performance.now() - init_date <= runningTime) {
/* no-op */
}
- assert.operator(mockP5Prototype.millis(), '>', runningTime, 'everything is ok');
+ assert.operator(
+ mockP5Prototype.millis(),
+ '>',
+ runningTime,
+ 'everything is ok'
+ );
});
- test.todo('result should be > newResult', function() {
+ test.todo('result should be > newResult', function () {
var runningTime = 50;
var init_date = Date.now();
const result = mockP5Prototype.millis();
diff --git a/test/unit/utilities/utility_functions.js b/test/unit/utilities/utility_functions.js
index 3b634ca496..cfdd1bb7a7 100644
--- a/test/unit/utilities/utility_functions.js
+++ b/test/unit/utilities/utility_functions.js
@@ -2,132 +2,132 @@ import { mockP5, mockP5Prototype } from '../../js/mocks';
import stringFunctions from '../../../src/utilities/utility_functions';
import random from '../../../src/math/random';
-suite('String functions', function() {
- beforeAll(function() {
+suite('String functions', function () {
+ beforeAll(function () {
stringFunctions(mockP5, mockP5Prototype);
random(mockP5, mockP5Prototype);
});
- suite('p5.prototype.nf', function() {
- test('should be a function', function() {
+ suite('p5.prototype.nf', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.nf);
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 1234;
const result = mockP5Prototype.nf(num, 3);
assert.equal(result, '1234');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 1234;
const result = mockP5Prototype.nf(num, 5);
assert.equal(result, '01234');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 1234;
const result = mockP5Prototype.nf(num, 3, 3);
assert.equal(result, '1234.000');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 3.141516;
const result = mockP5Prototype.nf(num, '2'); // automatic conversion?
assert.equal(result, '03.141516');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 3.141516;
const result = mockP5Prototype.nf(num, '2', '2'); // automatic conversion?
assert.equal(result, '03.14');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 3.141516e-2;
const result = mockP5Prototype.nf(num, '3', '4'); // automatic conversion?
assert.equal(result, '000.0314');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 3.141516e7;
const result = mockP5Prototype.nf(num, '3', '4'); // automatic conversion?
assert.equal(result, '31415160.0000');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 123.45;
const result = mockP5Prototype.nf(num, 3, 0);
assert.equal(result, '123');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = -123;
const result = mockP5Prototype.nf(num, 5);
assert.equal(result, '-00123');
});
});
- suite('p5.prototype.nfc', function() {
- test('should be a function', function() {
+ suite('p5.prototype.nfc', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.nfc);
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 32000;
const result = mockP5Prototype.nfc(num, 3);
assert.equal(result, '32,000.000');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 32000;
const result = mockP5Prototype.nfc(num, '3'); // automatic conversion?
assert.equal(result, '32,000.000');
});
});
- suite('p5.prototype.nfp', function() {
- test('should be a function', function() {
+ suite('p5.prototype.nfp', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.nfp);
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = -32000;
const result = mockP5Prototype.nfp(num, 3);
assert.equal(result, '-32000');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 32000;
const result = mockP5Prototype.nfp(num, 3); // automatic conversion?
assert.equal(result, '+32000');
});
});
- suite('p5.prototype.nfs', function() {
- test('should be a function', function() {
+ suite('p5.prototype.nfs', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.nfs);
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = -32000;
const result = mockP5Prototype.nfs(num, 3);
assert.equal(result, '-32000');
});
- test('should return correct string', function() {
+ test('should return correct string', function () {
var num = 32000;
const result = mockP5Prototype.nfs(num, 3); // automatic conversion?
assert.equal(result, ' 32000');
});
});
- suite('p5.prototype.splitTokens', function() {
- test('should be a function', function() {
+ suite('p5.prototype.splitTokens', function () {
+ test('should be a function', function () {
assert.ok(mockP5Prototype.splitTokens);
});
- test('should return correct index of match strings', function() {
+ test('should return correct index of match strings', function () {
var str = 'parsely, sage, rosemary, thyme';
var regexp = ',';
const result = mockP5Prototype.splitTokens(str, regexp);
@@ -135,8 +135,8 @@ suite('String functions', function() {
});
});
- suite('p5.prototype.shuffle', function() {
- test('should contain all the elements of the original array', function() {
+ suite('p5.prototype.shuffle', function () {
+ test('should contain all the elements of the original array', function () {
let regularArr = ['ABC', 'def', {}, Math.PI * 2, Math.E];
let newArr = mockP5Prototype.shuffle(regularArr);
let flag = true;
diff --git a/test/unit/visual/cases/noise.js b/test/unit/visual/cases/noise.js
index 11e64ef06f..e6e35e7387 100644
--- a/test/unit/visual/cases/noise.js
+++ b/test/unit/visual/cases/noise.js
@@ -1,7 +1,7 @@
import { visualSuite, visualTest } from '../visualTest';
-visualSuite('Noise', function() {
- visualTest('Drawn as values', function(p5, screenshot) {
+visualSuite('Noise', function () {
+ visualTest('Drawn as values', function (p5, screenshot) {
p5.createCanvas(50, 50);
p5.background(255);
p5.strokeWeight(2);
@@ -15,17 +15,24 @@ visualSuite('Noise', function() {
screenshot();
});
- visualTest('Drawn in a shader', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseFilterShader().modify(() => {
- p5.getColor(inputs => {
- const value = p5.clamp(p5.noise(inputs.texCoord * 4) * 2, 0, 1);
- return [value, value, value, 1];
- });
- }, { p5 });
- p5.filter(shader);
- screenshot();
- }, { skip: true });
+ visualTest(
+ 'Drawn in a shader',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const shader = p5.baseFilterShader().modify(
+ () => {
+ p5.getColor(inputs => {
+ const value = p5.clamp(p5.noise(inputs.texCoord * 4) * 2, 0, 1);
+ return [value, value, value, 1];
+ });
+ },
+ { p5 }
+ );
+ p5.filter(shader);
+ screenshot();
+ },
+ { skip: true }
+ );
// TODO(dave): Figure out why the noise shader looks different on CI and
// re-enable this test
});
diff --git a/test/unit/visual/cases/shape_modes.js b/test/unit/visual/cases/shape_modes.js
index 692ab48100..7c7506bcb5 100644
--- a/test/unit/visual/cases/shape_modes.js
+++ b/test/unit/visual/cases/shape_modes.js
@@ -13,8 +13,8 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) {
// Adjust coordinates for testing modes other than CORNERS
if (mode === p5.CORNER) {
// Find top left corner
- let x = p5.min(x1, x2); // x
- let y = p5.min(y1, y2); // y
+ let x = p5.min(x1, x2); // x
+ let y = p5.min(y1, y2); // y
// Calculate width and height
// Don't use abs(), so we get negative values as well
let w = x2 - x1; // w
@@ -22,10 +22,17 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) {
// With mode CORNER, rects with negative widths/heights result in mirrored/flipped rendering
// In this case, adjust position so the rect is in line with the other cases
if (shape === 'rect') {
- if (w < 0) { x += (-w); } // Move right
- if (h < 0) { y += (-h); } // Move down
+ if (w < 0) {
+ x += -w;
+ } // Move right
+ if (h < 0) {
+ y += -h;
+ } // Move down
}
- x1 = x; y1 = y; x2 = w; y2 = h;
+ x1 = x;
+ y1 = y;
+ x2 = w;
+ y2 = h;
} else if (mode === p5.CENTER) {
// Find center
let x = (x2 + x1) / 2; // x
@@ -34,7 +41,10 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) {
// Don't use abs(), so we get negative values as well
let w = x2 - x1;
let h = y2 - y1;
- x1 = x; y1 = y; x2 = w; y2 = h;
+ x1 = x;
+ y1 = y;
+ x2 = w;
+ y2 = h;
} else if (mode === p5.RADIUS) {
// Find Center
let x = (x2 + x1) / 2; // x
@@ -43,7 +53,10 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) {
// Don't use abs(), so we get negative values as well
let r1 = (x2 - x1) / 2; // r1;
let r2 = (y2 - y1) / 2; // r2
- x1 = x; y1 = y; x2 = r1; y2 = r2;
+ x1 = x;
+ y1 = y;
+ x2 = r1;
+ y2 = r2;
}
if (shape === 'ellipse') {
@@ -63,8 +76,7 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) {
}
}
-
-visualSuite('Shape Modes', function() {
+visualSuite('Shape Modes', function () {
/*
Comprehensive test for rendering ellipse(), arc(), and rect()
with the different ellipseMode() / rectMode() values: CORNERS, CORNER, CENTER, RADIUS.
@@ -74,18 +86,17 @@ visualSuite('Shape Modes', function() {
*/
// Shapes to test
- const SHAPES = [ 'ellipse', 'arc', 'rect' ];
+ const SHAPES = ['ellipse', 'arc', 'rect'];
// Modes to test (used with ellipseMode or rectMode, according to shape)
- const MODES = [ 'CORNERS', 'CORNER', 'CENTER', 'RADIUS' ];
+ const MODES = ['CORNERS', 'CORNER', 'CENTER', 'RADIUS'];
for (let shape of SHAPES) {
- visualSuite(`Shape ${shape}`, function() {
-
+ visualSuite(`Shape ${shape}`, function () {
for (let mode of MODES) {
- visualTest(`Mode ${mode}`, function(p5, screenshot) {
+ visualTest(`Mode ${mode}`, function (p5, screenshot) {
p5.createCanvas(60, 125);
- p5.translate(p5.width/2, p5.height/2);
+ p5.translate(p5.width / 2, p5.height / 2);
// Make the following calls to shapeCorners shorter
// by omitting p5, shape and mode parameters
@@ -95,87 +106,84 @@ visualSuite('Shape Modes', function() {
// Quadrant I (Bottom Right)
// P1 P2
- _shapeCorners( 5, 5, 25, 15); // P1 Top Left, P2 Bottom Right
- _shapeCorners( 5, 20, 25, 30); // P1 Bottom Left, P2 Top Right
- _shapeCorners(25, 45, 5, 35); // P1 Bottom Right, P2 Top Left
- _shapeCorners(25, 50, 5, 60); // P1 Top Right, P2 Bottom Left
+ _shapeCorners(5, 5, 25, 15); // P1 Top Left, P2 Bottom Right
+ _shapeCorners(5, 20, 25, 30); // P1 Bottom Left, P2 Top Right
+ _shapeCorners(25, 45, 5, 35); // P1 Bottom Right, P2 Top Left
+ _shapeCorners(25, 50, 5, 60); // P1 Top Right, P2 Bottom Left
// Quadrant II (Bottom Left)
- _shapeCorners(-25, 5, -5, 15);
- _shapeCorners(-25, 20, -5, 30);
- _shapeCorners( -5, 45, -25, 35);
- _shapeCorners( -5, 50, -25, 60);
+ _shapeCorners(-25, 5, -5, 15);
+ _shapeCorners(-25, 20, -5, 30);
+ _shapeCorners(-5, 45, -25, 35);
+ _shapeCorners(-5, 50, -25, 60);
// Quadrant III (Top Left)
- _shapeCorners(-25, -60, -5, -50);
- _shapeCorners(-25, -35, -5, -45);
- _shapeCorners( -5, -20, -25, -30);
- _shapeCorners( -5, -15, -25, -5);
+ _shapeCorners(-25, -60, -5, -50);
+ _shapeCorners(-25, -35, -5, -45);
+ _shapeCorners(-5, -20, -25, -30);
+ _shapeCorners(-5, -15, -25, -5);
// Quadrant IV (Top Right)
- _shapeCorners( 5, -60, 25, -50);
- _shapeCorners( 5, -35, 25, -45);
- _shapeCorners(25, -20, 5, -30);
- _shapeCorners(25, -15, 5, -5);
+ _shapeCorners(5, -60, 25, -50);
+ _shapeCorners(5, -35, 25, -45);
+ _shapeCorners(25, -20, 5, -30);
+ _shapeCorners(25, -15, 5, -5);
screenshot();
}); // End of: visualTest
} // End of: MODES loop
-
}); // End of: visualSuite
} // End of: SHAPES loop
-
/*
An extra test suite specific to shape mode CORNER and negative dimensions.
For rect, negative widths/heights result in flipped rendering (horizontally/vertically)
For ellipse and arc, using negative widths/heights has no effect (the absolute value is used)
*/
- visualSuite('Negative dimensions', function() {
+ visualSuite('Negative dimensions', function () {
// Negative widths/height result in flipped rects.
- visualTest('rect', function(p5, screenshot) {
+ visualTest('rect', function (p5, screenshot) {
p5.createCanvas(50, 50);
- p5.translate(p5.width/2, p5.height/2);
+ p5.translate(p5.width / 2, p5.height / 2);
p5.rectMode(p5.CORNER);
- p5.rect(0, 0, 20, 10);
+ p5.rect(0, 0, 20, 10);
p5.fill('red');
- p5.rect(0, 0, -20, 10);
+ p5.rect(0, 0, -20, 10);
p5.fill('green');
- p5.rect(0, 0, 20, -10);
+ p5.rect(0, 0, 20, -10);
p5.fill('blue');
p5.rect(0, 0, -20, -10);
screenshot();
});
// Since negative widths/heights are used with their absolute value,
// ellipses are drawn on top of each other, blue one last
- visualTest('ellipse', function(p5, screenshot) {
+ visualTest('ellipse', function (p5, screenshot) {
p5.createCanvas(50, 50);
- p5.translate(p5.width/2, p5.height/2);
+ p5.translate(p5.width / 2, p5.height / 2);
p5.ellipseMode(p5.CORNER);
- p5.ellipse(0, 0, 20, 10);
+ p5.ellipse(0, 0, 20, 10);
p5.fill('red');
- p5.ellipse(0, 0, -20, 10);
+ p5.ellipse(0, 0, -20, 10);
p5.fill('green');
- p5.ellipse(0, 0, 20, -10);
+ p5.ellipse(0, 0, 20, -10);
p5.fill('blue');
p5.ellipse(0, 0, -20, -10);
screenshot();
});
// Since negative widths/heights are used with their absolute value,
// arcs are drawn on top of each other, blue one last.
- visualTest('arc', function(p5, screenshot) {
+ visualTest('arc', function (p5, screenshot) {
p5.createCanvas(50, 50);
- p5.translate(p5.width/2, p5.height/2);
+ p5.translate(p5.width / 2, p5.height / 2);
p5.ellipseMode(p5.CORNER);
- p5.arc(0, 0, 20, 10, 0, p5.PI + p5.HALF_PI);
+ p5.arc(0, 0, 20, 10, 0, p5.PI + p5.HALF_PI);
p5.fill('red');
- p5.arc(0, 0, -20, 10, 0, p5.PI + p5.HALF_PI);
+ p5.arc(0, 0, -20, 10, 0, p5.PI + p5.HALF_PI);
p5.fill('green');
- p5.arc(0, 0, 20, -10, 0, p5.PI + p5.HALF_PI);
+ p5.arc(0, 0, 20, -10, 0, p5.PI + p5.HALF_PI);
p5.fill('blue');
p5.arc(0, 0, -20, -10, 0, p5.PI + p5.HALF_PI);
screenshot();
});
});
-
});
diff --git a/test/unit/visual/cases/shapes.js b/test/unit/visual/cases/shapes.js
index 2c245dbec2..b3fc2dede8 100644
--- a/test/unit/visual/cases/shapes.js
+++ b/test/unit/visual/cases/shapes.js
@@ -1,8 +1,8 @@
import { visualSuite, visualTest } from '../visualTest';
-visualSuite('Shape drawing', function() {
+visualSuite('Shape drawing', function () {
for (const mode of ['2D', 'WebGL']) {
- visualSuite(`${mode} mode`, function() {
+ visualSuite(`${mode} mode`, function () {
const setup = p5 => {
p5.createCanvas(50, 50, mode === '2D' ? p5.P2D : p5.WEBGL);
if (mode !== '2D') {
@@ -13,7 +13,7 @@ visualSuite('Shape drawing', function() {
p5.stroke(0);
};
- visualTest('Drawing polylines', function(p5, screenshot) {
+ visualTest('Drawing polylines', function (p5, screenshot) {
setup(p5);
p5.beginShape();
p5.vertex(10, 10);
@@ -25,7 +25,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with contours', function(p5, screenshot) {
+ visualTest('Drawing with contours', function (p5, screenshot) {
setup(p5);
const vertexCircle = (x, y, r, direction) => {
@@ -52,19 +52,19 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing triangle fans', function(p5, screenshot) {
+ visualTest('Drawing triangle fans', function (p5, screenshot) {
setup(p5);
p5.beginShape(p5.TRIANGLE_FAN);
p5.vertex(25, 25);
for (let i = 0; i <= 12; i++) {
const angle = p5.map(i, 0, 12, 0, p5.TWO_PI);
- p5.vertex(25 + 10*p5.cos(angle), 25 + 10*p5.sin(angle));
+ p5.vertex(25 + 10 * p5.cos(angle), 25 + 10 * p5.sin(angle));
}
p5.endShape();
screenshot();
});
- visualTest('Drawing triangle strips', function(p5, screenshot) {
+ visualTest('Drawing triangle strips', function (p5, screenshot) {
setup(p5);
p5.beginShape(p5.TRIANGLE_STRIP);
p5.vertex(10, 10);
@@ -77,7 +77,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing quad strips', function(p5, screenshot) {
+ visualTest('Drawing quad strips', function (p5, screenshot) {
setup(p5);
p5.beginShape(p5.QUAD_STRIP);
p5.vertex(10, 10);
@@ -90,7 +90,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing closed polylines', function(p5, screenshot) {
+ visualTest('Drawing closed polylines', function (p5, screenshot) {
setup(p5);
p5.beginShape();
p5.vertex(10, 10);
@@ -102,7 +102,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with curves', function(p5, screenshot) {
+ visualTest('Drawing with curves', function (p5, screenshot) {
setup(p5);
p5.beginShape();
p5.splineVertex(10, 10);
@@ -114,31 +114,37 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with curves in the middle of other shapes', function(p5, screenshot) {
- setup(p5);
- p5.beginShape();
- p5.vertex(10, 10);
- p5.vertex(40, 10);
- p5.splineVertex(40, 40);
- p5.splineVertex(10, 40);
- p5.endShape(p5.CLOSE);
- screenshot();
- });
+ visualTest(
+ 'Drawing with curves in the middle of other shapes',
+ function (p5, screenshot) {
+ setup(p5);
+ p5.beginShape();
+ p5.vertex(10, 10);
+ p5.vertex(40, 10);
+ p5.splineVertex(40, 40);
+ p5.splineVertex(10, 40);
+ p5.endShape(p5.CLOSE);
+ screenshot();
+ }
+ );
- visualTest('Drawing with curves with hidden ends', function(p5, screenshot) {
- setup(p5);
- p5.beginShape();
- p5.splineProperty('ends', p5.EXCLUDE);
- p5.splineVertex(10, 10);
- p5.splineVertex(15, 40);
- p5.splineVertex(40, 35);
- p5.splineVertex(25, 15);
- p5.splineVertex(15, 25);
- p5.endShape();
- screenshot();
- });
+ visualTest(
+ 'Drawing with curves with hidden ends',
+ function (p5, screenshot) {
+ setup(p5);
+ p5.beginShape();
+ p5.splineProperty('ends', p5.EXCLUDE);
+ p5.splineVertex(10, 10);
+ p5.splineVertex(15, 40);
+ p5.splineVertex(40, 35);
+ p5.splineVertex(25, 15);
+ p5.splineVertex(15, 25);
+ p5.endShape();
+ screenshot();
+ }
+ );
- visualTest('Drawing closed curves', function(p5, screenshot) {
+ visualTest('Drawing closed curves', function (p5, screenshot) {
setup(p5);
p5.beginShape();
p5.splineVertex(10, 10);
@@ -150,7 +156,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing simple closed curves', function(p5, screenshot) {
+ visualTest('Drawing simple closed curves', function (p5, screenshot) {
setup(p5);
p5.beginShape();
p5.splineVertex(10, 10);
@@ -160,20 +166,23 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with curves with tightness', function(p5, screenshot) {
- setup(p5);
- p5.splineProperty('tightness', -1);
- p5.beginShape();
- p5.splineVertex(10, 10);
- p5.splineVertex(15, 40);
- p5.splineVertex(40, 35);
- p5.splineVertex(25, 15);
- p5.splineVertex(15, 25);
- p5.endShape();
- screenshot();
- });
+ visualTest(
+ 'Drawing with curves with tightness',
+ function (p5, screenshot) {
+ setup(p5);
+ p5.splineProperty('tightness', -1);
+ p5.beginShape();
+ p5.splineVertex(10, 10);
+ p5.splineVertex(15, 40);
+ p5.splineVertex(40, 35);
+ p5.splineVertex(25, 15);
+ p5.splineVertex(15, 25);
+ p5.endShape();
+ screenshot();
+ }
+ );
- visualTest('Drawing closed curve loops', function(p5, screenshot) {
+ visualTest('Drawing closed curve loops', function (p5, screenshot) {
setup(p5);
p5.beginShape();
p5.splineProperty('ends', p5.EXCLUDE);
@@ -190,7 +199,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with cubic beziers', function(p5, screenshot) {
+ visualTest('Drawing with cubic beziers', function (p5, screenshot) {
setup(p5);
p5.beginShape();
p5.bezierVertex(10, 10);
@@ -206,7 +215,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with quadratic beziers', function(p5, screenshot) {
+ visualTest('Drawing with quadratic beziers', function (p5, screenshot) {
setup(p5);
p5.beginShape();
p5.bezierOrder(2);
@@ -224,32 +233,35 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Combining quadratic and cubic beziers', function (p5, screenshot) {
- setup(p5);
- p5.strokeWeight(5);
- p5.beginShape();
- p5.vertex(10, 10);
- p5.vertex(30, 10);
+ visualTest(
+ 'Combining quadratic and cubic beziers',
+ function (p5, screenshot) {
+ setup(p5);
+ p5.strokeWeight(5);
+ p5.beginShape();
+ p5.vertex(10, 10);
+ p5.vertex(30, 10);
- // Default cubic
- p5.bezierVertex(35, 10);
- p5.bezierVertex(40, 15);
- p5.bezierVertex(40, 20);
+ // Default cubic
+ p5.bezierVertex(35, 10);
+ p5.bezierVertex(40, 15);
+ p5.bezierVertex(40, 20);
- p5.vertex(40, 30);
+ p5.vertex(40, 30);
- p5.bezierOrder(2);
- p5.bezierVertex(40, 40);
- p5.bezierVertex(30, 40);
+ p5.bezierOrder(2);
+ p5.bezierVertex(40, 40);
+ p5.bezierVertex(30, 40);
- p5.vertex(10, 40);
+ p5.vertex(10, 40);
- p5.endShape(p5.CLOSE);
+ p5.endShape(p5.CLOSE);
- screenshot();
- });
+ screenshot();
+ }
+ );
- visualTest('Drawing with points', function(p5, screenshot) {
+ visualTest('Drawing with points', function (p5, screenshot) {
setup(p5);
p5.strokeWeight(5);
p5.beginShape(p5.POINTS);
@@ -262,7 +274,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with lines', function(p5, screenshot) {
+ visualTest('Drawing with lines', function (p5, screenshot) {
setup(p5);
p5.beginShape(p5.LINES);
p5.vertex(10, 10);
@@ -273,7 +285,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with triangles', function(p5, screenshot) {
+ visualTest('Drawing with triangles', function (p5, screenshot) {
setup(p5);
p5.beginShape(p5.TRIANGLES);
p5.vertex(10, 10);
@@ -286,7 +298,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing with quads', function(p5, screenshot) {
+ visualTest('Drawing with quads', function (p5, screenshot) {
setup(p5);
p5.beginShape(p5.QUADS);
p5.vertex(10, 10);
@@ -301,74 +313,83 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Drawing rounded rectangles', function(p5, screenshot) {
+ visualTest('Drawing rounded rectangles', function (p5, screenshot) {
setup(p5);
p5.rect(5, 5, 40, 20, 5);
p5.rect(5, 30, 40, 15, 2, 4, 6, 8);
screenshot();
});
- visualTest('Drawing with a single closed contour', function(p5, screenshot) {
- setup(p5);
- p5.beginShape();
- p5.vertex(10, 10);
- p5.vertex(40, 10);
- p5.vertex(40, 40);
- p5.vertex(10, 40);
-
- p5.beginContour();
- p5.vertex(20, 20);
- p5.vertex(20, 30);
- p5.vertex(30, 30);
- p5.vertex(30, 20);
- p5.endContour(p5.CLOSE);
+ visualTest(
+ 'Drawing with a single closed contour',
+ function (p5, screenshot) {
+ setup(p5);
+ p5.beginShape();
+ p5.vertex(10, 10);
+ p5.vertex(40, 10);
+ p5.vertex(40, 40);
+ p5.vertex(10, 40);
- p5.endShape(p5.CLOSE);
- screenshot();
- });
+ p5.beginContour();
+ p5.vertex(20, 20);
+ p5.vertex(20, 30);
+ p5.vertex(30, 30);
+ p5.vertex(30, 20);
+ p5.endContour(p5.CLOSE);
- visualTest('Drawing with a single unclosed contour', function(p5, screenshot) {
- setup(p5);
- p5.beginShape();
- p5.vertex(10, 10);
- p5.vertex(40, 10);
- p5.vertex(40, 40);
- p5.vertex(10, 40);
+ p5.endShape(p5.CLOSE);
+ screenshot();
+ }
+ );
- p5.beginContour();
- p5.vertex(20, 20);
- p5.vertex(20, 30);
- p5.vertex(30, 30);
- p5.vertex(30, 20);
- p5.endContour();
+ visualTest(
+ 'Drawing with a single unclosed contour',
+ function (p5, screenshot) {
+ setup(p5);
+ p5.beginShape();
+ p5.vertex(10, 10);
+ p5.vertex(40, 10);
+ p5.vertex(40, 40);
+ p5.vertex(10, 40);
- p5.endShape(p5.CLOSE);
- screenshot();
- });
+ p5.beginContour();
+ p5.vertex(20, 20);
+ p5.vertex(20, 30);
+ p5.vertex(30, 30);
+ p5.vertex(30, 20);
+ p5.endContour();
- visualTest('Drawing with every subshape in a contour', function(p5, screenshot) {
- setup(p5);
- p5.beginShape();
- p5.beginContour();
- p5.vertex(10, 10);
- p5.vertex(40, 10);
- p5.vertex(40, 40);
- p5.vertex(10, 40);
- p5.endContour(p5.CLOSE);
+ p5.endShape(p5.CLOSE);
+ screenshot();
+ }
+ );
- p5.beginContour();
- p5.vertex(20, 20);
- p5.vertex(20, 30);
- p5.vertex(30, 30);
- p5.vertex(30, 20);
- p5.endContour(p5.CLOSE);
+ visualTest(
+ 'Drawing with every subshape in a contour',
+ function (p5, screenshot) {
+ setup(p5);
+ p5.beginShape();
+ p5.beginContour();
+ p5.vertex(10, 10);
+ p5.vertex(40, 10);
+ p5.vertex(40, 40);
+ p5.vertex(10, 40);
+ p5.endContour(p5.CLOSE);
+
+ p5.beginContour();
+ p5.vertex(20, 20);
+ p5.vertex(20, 30);
+ p5.vertex(30, 30);
+ p5.vertex(30, 20);
+ p5.endContour(p5.CLOSE);
- p5.endShape();
- screenshot();
- });
+ p5.endShape();
+ screenshot();
+ }
+ );
if (mode === 'WebGL') {
- visualTest('3D vertex coordinates', function(p5, screenshot) {
+ visualTest('3D vertex coordinates', function (p5, screenshot) {
setup(p5);
p5.beginShape(p5.QUAD_STRIP);
@@ -381,7 +402,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('3D quadratic coordinates', function(p5, screenshot) {
+ visualTest('3D quadratic coordinates', function (p5, screenshot) {
setup(p5);
p5.beginShape();
@@ -395,7 +416,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('3D cubic coordinates', function(p5, screenshot) {
+ visualTest('3D cubic coordinates', function (p5, screenshot) {
setup(p5);
p5.beginShape();
@@ -409,7 +430,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Texture coordinates', async function(p5, screenshot) {
+ visualTest('Texture coordinates', async function (p5, screenshot) {
const tex = await p5.loadImage('test/unit/assets/cat.jpg');
setup(p5);
p5.texture(tex);
@@ -423,22 +444,25 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Normalized texture coordinates', async function(p5, screenshot) {
- const tex = await p5.loadImage('test/unit/assets/cat.jpg');
- setup(p5);
- p5.texture(tex);
- p5.textureMode(p5.NORMAL);
- p5.beginShape(p5.QUAD_STRIP);
- p5.vertex(10, 10, 0, 0, 0);
- p5.vertex(45, 5, 0, 1, 0);
- p5.vertex(15, 35, 0, 0, 1);
- p5.vertex(40, 45, 0, 1, 1);
- p5.endShape();
-
- screenshot();
- });
+ visualTest(
+ 'Normalized texture coordinates',
+ async function (p5, screenshot) {
+ const tex = await p5.loadImage('test/unit/assets/cat.jpg');
+ setup(p5);
+ p5.texture(tex);
+ p5.textureMode(p5.NORMAL);
+ p5.beginShape(p5.QUAD_STRIP);
+ p5.vertex(10, 10, 0, 0, 0);
+ p5.vertex(45, 5, 0, 1, 0);
+ p5.vertex(15, 35, 0, 0, 1);
+ p5.vertex(40, 45, 0, 1, 1);
+ p5.endShape();
+
+ screenshot();
+ }
+ );
- visualTest('Per-vertex fills', function(p5, screenshot) {
+ visualTest('Per-vertex fills', function (p5, screenshot) {
setup(p5);
p5.beginShape(p5.QUAD_STRIP);
p5.fill(0);
@@ -454,7 +478,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Per-vertex strokes', function(p5, screenshot) {
+ visualTest('Per-vertex strokes', function (p5, screenshot) {
setup(p5);
p5.strokeWeight(5);
p5.beginShape(p5.QUAD_STRIP);
@@ -471,7 +495,7 @@ visualSuite('Shape drawing', function() {
screenshot();
});
- visualTest('Per-vertex normals', function(p5, screenshot) {
+ visualTest('Per-vertex normals', function (p5, screenshot) {
setup(p5);
p5.normalMaterial();
p5.beginShape(p5.QUAD_STRIP);
diff --git a/test/unit/visual/cases/typography.js b/test/unit/visual/cases/typography.js
index 10ba96d7bf..02b742fbfa 100644
--- a/test/unit/visual/cases/typography.js
+++ b/test/unit/visual/cases/typography.js
@@ -1,690 +1,767 @@
import { visualSuite, visualTest } from '../visualTest';
-visualSuite('Typography', function () {
- visualSuite('textFont', function () {
- visualTest('with the default font', function (p5, screenshot) {
- p5.createCanvas(50, 50);
- p5.textSize(20);
- p5.textAlign(p5.LEFT, p5.TOP);
- p5.text('test', 0, 0);
- screenshot();
- });
-
- visualTest('with the default monospace font', function (p5, screenshot) {
- p5.createCanvas(50, 50);
- p5.textSize(20);
- p5.textFont('monospace');
- p5.textAlign(p5.LEFT, p5.TOP);
- p5.text('test', 0, 0);
- screenshot();
- });
-
- visualTest('with a Google Font URL', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&display=swap'
- );
- p5.textFont(font);
- p5.textAlign(p5.LEFT, p5.TOP);
- p5.textSize(35);
- p5.text('p5*js', 0, 10, p5.width);
- screenshot();
- });
-
- visualTest('with a font file', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.textFont(font);
- p5.textAlign(p5.LEFT, p5.TOP);
- p5.textSize(35);
- p5.text('p5*js', 0, 10, p5.width);
- screenshot();
- });
-
- visualTest('with a font file and special chars', async function (p5, screenshot) {
- p5.createCanvas(100, 100, p5.WEBGL);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf',
- 'Incönsolata'
- );
- p5.textFont(font);
- p5.textAlign(p5.CENTER, p5.CENTER);
- p5.textSize(35);
- p5.text('p5*js', 0, 0);
- p5.noFill();
- p5.rectMode(p5.CENTER);
- p5.rect(0, 0, p5.fontWidth('p5*js'), p5.textLeading());
- screenshot();
- });
-
- visualTest('with a woff font file', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'test/unit/assets/Lato-Regular.woff'
- );
- p5.textFont(font);
- p5.textAlign(p5.LEFT, p5.TOP);
- p5.textSize(35);
- p5.text('p5*js', 0, 10, p5.width);
- screenshot();
- });
-
- visualTest('with a directly set font string', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- p5.textFont('italic bold 32px serif');
- p5.text('p5*js', 0, 10, p5.width);
- screenshot();
- });
-
- visualTest('with a font file in WebGL', async function (p5, screenshot) {
- p5.createCanvas(100, 100, p5.WEBGL);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.textFont(font);
- p5.textAlign(p5.LEFT, p5.TOP);
- p5.textSize(35);
- p5.text('p5*js', -p5.width / 2, -p5.height / 2 + 10, p5.width);
- screenshot();
- });
- });
+visualSuite(
+ 'Typography',
+ function () {
+ visualSuite('textFont', function () {
+ visualTest('with the default font', function (p5, screenshot) {
+ p5.createCanvas(50, 50);
+ p5.textSize(20);
+ p5.textAlign(p5.LEFT, p5.TOP);
+ p5.text('test', 0, 0);
+ screenshot();
+ });
- visualSuite('textWeight', function () {
- visualTest('can control non-variable fonts', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'https://fonts.googleapis.com/css2?family=Sniglet:wght@400;800&display=swap'
- );
+ visualTest('with the default monospace font', function (p5, screenshot) {
+ p5.createCanvas(50, 50);
+ p5.textSize(20);
+ p5.textFont('monospace');
+ p5.textAlign(p5.LEFT, p5.TOP);
+ p5.text('test', 0, 0);
+ screenshot();
+ });
- for (const weight of [400, 800]) {
- p5.background(255);
+ visualTest('with a Google Font URL', async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont(
+ 'https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&display=swap'
+ );
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
- p5.textWeight(weight);
p5.text('p5*js', 0, 10, p5.width);
screenshot();
- }
- });
+ });
- visualTest('can control variable fonts', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'
- );
- for (let weight = 400; weight <= 800; weight += 100) {
- p5.background(255);
+ visualTest('with a font file', async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont('test/unit/assets/Inconsolata-Bold.ttf');
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
- p5.textWeight(weight);
p5.text('p5*js', 0, 10, p5.width);
screenshot();
- }
- });
+ });
- visualTest('can control variable fonts from files', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'test/unit/assets/BricolageGrotesque-Variable.ttf'
+ visualTest(
+ 'with a font file and special chars',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100, p5.WEBGL);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf',
+ 'Incönsolata'
+ );
+ p5.textFont(font);
+ p5.textAlign(p5.CENTER, p5.CENTER);
+ p5.textSize(35);
+ p5.text('p5*js', 0, 0);
+ p5.noFill();
+ p5.rectMode(p5.CENTER);
+ p5.rect(0, 0, p5.fontWidth('p5*js'), p5.textLeading());
+ screenshot();
+ }
);
- for (let weight = 400; weight <= 800; weight += 100) {
- p5.background(255);
+
+ visualTest('with a woff font file', async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont('test/unit/assets/Lato-Regular.woff');
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
- p5.textWeight(weight);
p5.text('p5*js', 0, 10, p5.width);
screenshot();
- }
- });
+ });
- visualTest('can control variable fonts from files in WebGL', async function (p5, screenshot) {
- p5.createCanvas(100, 100, p5.WEBGL);
- const font = await p5.loadFont(
- 'test/unit/assets/BricolageGrotesque-Variable.ttf'
+ visualTest(
+ 'with a directly set font string',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ p5.textFont('italic bold 32px serif');
+ p5.text('p5*js', 0, 10, p5.width);
+ screenshot();
+ }
);
- for (let weight = 400; weight <= 800; weight += 100) {
- p5.push();
- p5.background(255);
- p5.translate(-p5.width/2, -p5.height/2);
+
+ visualTest('with a font file in WebGL', async function (p5, screenshot) {
+ p5.createCanvas(100, 100, p5.WEBGL);
+ const font = await p5.loadFont('test/unit/assets/Inconsolata-Bold.ttf');
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
- p5.textWeight(weight);
- p5.text('p5*js', 0, 10, p5.width);
- p5.pop();
+ p5.text('p5*js', -p5.width / 2, -p5.height / 2 + 10, p5.width);
screenshot();
- }
+ });
});
- });
-
- visualSuite('vertical centering', function() {
- const fonts = {
- Inter: 'https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfMZhrib2Bg-4.ttf',
- Raleway: 'https://fonts.gstatic.com/s/raleway/v36/1Ptxg8zYS_SKggPN4iEgvnHyvveLxVvaooCPNLA3JC9c.ttf',
- 'Inknut Antiqua': 'https://fonts.gstatic.com/s/inknutantiqua/v16/Y4GRYax7VC4ot_qNB4nYpBdaKU2_xbj5bBoIYJNf.ttf',
- Oswald: 'https://fonts.gstatic.com/s/oswald/v57/TK3_WkUHHAIjg75cFRf3bXL8LICs1_FvgUFoZAaRliE.ttf',
- };
- for (const mode of ['2d', 'webgl']) {
- for (const fontName in fonts) {
- visualTest(`${fontName} in ${mode} mode`, async function (p5, screenshot) {
- p5.createCanvas(200, 50, mode === 'webgl' ? p5.WEBGL : p5.P2D);
- p5.background(255);
- const font = await p5.loadFont(fonts[fontName]);
- if (mode === '2d') p5.translate(p5.width/2, p5.height/2);
- p5.textFont(font);
- p5.stroke('red');
- p5.line(-p5.width/2, 0, p5.width/2, 0);
+ visualSuite('textWeight', function () {
+ visualTest(
+ 'can control non-variable fonts',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont(
+ 'https://fonts.googleapis.com/css2?family=Sniglet:wght@400;800&display=swap'
+ );
- p5.noStroke();
- p5.fill(0);
- p5.textSize(20);
- p5.textAlign(p5.CENTER, p5.CENTER);
- p5.text('Vertical align', 0, 0);
+ for (const weight of [400, 800]) {
+ p5.background(255);
+ p5.textFont(font);
+ p5.textAlign(p5.LEFT, p5.TOP);
+ p5.textSize(35);
+ p5.textWeight(weight);
+ p5.text('p5*js', 0, 10, p5.width);
+ screenshot();
+ }
+ }
+ );
+
+ visualTest('can control variable fonts', async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont(
+ 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'
+ );
+ for (let weight = 400; weight <= 800; weight += 100) {
+ p5.background(255);
+ p5.textFont(font);
+ p5.textAlign(p5.LEFT, p5.TOP);
+ p5.textSize(35);
+ p5.textWeight(weight);
+ p5.text('p5*js', 0, 10, p5.width);
screenshot();
- });
- }
- }
- });
-
- visualSuite('textAlign', function () {
- for (const mode of ['2d', 'webgl']) {
- visualSuite(`${mode} mode`, () => {
- visualTest('all alignments with single word', async function (p5, screenshot) {
- const alignments = [
- { alignX: p5.LEFT, alignY: p5.TOP },
- { alignX: p5.CENTER, alignY: p5.TOP },
- { alignX: p5.RIGHT, alignY: p5.TOP },
- { alignX: p5.LEFT, alignY: p5.CENTER },
- { alignX: p5.CENTER, alignY: p5.CENTER },
- { alignX: p5.RIGHT, alignY: p5.CENTER },
- { alignX: p5.LEFT, alignY: p5.BOTTOM },
- { alignX: p5.CENTER, alignY: p5.BOTTOM },
- { alignX: p5.RIGHT, alignY: p5.BOTTOM }
- ];
-
- p5.createCanvas(300, 300, mode === 'webgl' ? p5.WEBGL : undefined);
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
- p5.textSize(60);
+ }
+ });
+
+ visualTest(
+ 'can control variable fonts from files',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
+ 'test/unit/assets/BricolageGrotesque-Variable.ttf'
);
- p5.textFont(font);
- alignments.forEach(alignment => {
+ for (let weight = 400; weight <= 800; weight += 100) {
p5.background(255);
- p5.textAlign(alignment.alignX, alignment.alignY);
- p5.text('Single Line', p5.width / 2, p5.height / 2);
- const bb = p5.textBounds('Single Line', p5.width / 2, p5.height / 2);
- p5.push();
- p5.noFill();
- p5.stroke('red');
- p5.rect(bb.x, bb.y, bb.w, bb.h);
- p5.pop();
+ p5.textFont(font);
+ p5.textAlign(p5.LEFT, p5.TOP);
+ p5.textSize(35);
+ p5.textWeight(weight);
+ p5.text('p5*js', 0, 10, p5.width);
screenshot();
- });
- });
+ }
+ }
+ );
- visualTest('all alignments with single line', async function (p5, screenshot) {
- const alignments = [
- { alignX: p5.LEFT, alignY: p5.TOP },
- { alignX: p5.CENTER, alignY: p5.TOP },
- { alignX: p5.RIGHT, alignY: p5.TOP },
- { alignX: p5.LEFT, alignY: p5.CENTER },
- { alignX: p5.CENTER, alignY: p5.CENTER },
- { alignX: p5.RIGHT, alignY: p5.CENTER },
- { alignX: p5.LEFT, alignY: p5.BOTTOM },
- { alignX: p5.CENTER, alignY: p5.BOTTOM },
- { alignX: p5.RIGHT, alignY: p5.BOTTOM }
- ];
-
- p5.createCanvas(300, 300, mode === 'webgl' ? p5.WEBGL : undefined);
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
- p5.textSize(45);
+ visualTest(
+ 'can control variable fonts from files in WebGL',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100, p5.WEBGL);
const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
+ 'test/unit/assets/BricolageGrotesque-Variable.ttf'
);
- p5.textFont(font);
- alignments.forEach(alignment => {
- p5.background(255);
- p5.textAlign(alignment.alignX, alignment.alignY);
- p5.text('Single Line', p5.width / 2, p5.height / 2);
- const bb = p5.textBounds('Single Line', p5.width / 2, p5.height / 2);
+ for (let weight = 400; weight <= 800; weight += 100) {
p5.push();
- p5.noFill();
- p5.stroke('red');
- p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.background(255);
+ p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textFont(font);
+ p5.textAlign(p5.LEFT, p5.TOP);
+ p5.textSize(35);
+ p5.textWeight(weight);
+ p5.text('p5*js', 0, 10, p5.width);
p5.pop();
screenshot();
- });
- });
-
- visualTest('all alignments with multi-lines and wrap word',
- async function (p5, screenshot) {
- const alignments = [
- { alignX: p5.LEFT, alignY: p5.TOP },
- { alignX: p5.CENTER, alignY: p5.TOP },
- { alignX: p5.RIGHT, alignY: p5.TOP },
- { alignX: p5.LEFT, alignY: p5.CENTER },
- { alignX: p5.CENTER, alignY: p5.CENTER },
- { alignX: p5.RIGHT, alignY: p5.CENTER },
- { alignX: p5.LEFT, alignY: p5.BOTTOM },
- { alignX: p5.CENTER, alignY: p5.BOTTOM },
- { alignX: p5.RIGHT, alignY: p5.BOTTOM }
- ];
-
- p5.createCanvas(150, 100, mode === 'webgl' ? p5.WEBGL : undefined);
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
- p5.textSize(20);
- p5.textWrap(p5.WORD);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.textFont(font);
-
- let xPos = 20;
- let yPos = 20;
- const boxWidth = 100;
- const boxHeight = 60;
+ }
+ }
+ );
+ });
- alignments.forEach((alignment, i) => {
+ visualSuite('vertical centering', function () {
+ const fonts = {
+ Inter:
+ 'https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfMZhrib2Bg-4.ttf',
+ Raleway:
+ 'https://fonts.gstatic.com/s/raleway/v36/1Ptxg8zYS_SKggPN4iEgvnHyvveLxVvaooCPNLA3JC9c.ttf',
+ 'Inknut Antiqua':
+ 'https://fonts.gstatic.com/s/inknutantiqua/v16/Y4GRYax7VC4ot_qNB4nYpBdaKU2_xbj5bBoIYJNf.ttf',
+ Oswald:
+ 'https://fonts.gstatic.com/s/oswald/v57/TK3_WkUHHAIjg75cFRf3bXL8LICs1_FvgUFoZAaRliE.ttf'
+ };
+ for (const mode of ['2d', 'webgl']) {
+ for (const fontName in fonts) {
+ visualTest(
+ `${fontName} in ${mode} mode`,
+ async function (p5, screenshot) {
+ p5.createCanvas(200, 50, mode === 'webgl' ? p5.WEBGL : p5.P2D);
p5.background(255);
- p5.push();
- p5.textAlign(alignment.alignX, alignment.alignY);
-
- p5.noFill();
- p5.strokeWeight(2);
- p5.stroke(200);
- p5.rect(xPos, yPos, boxWidth, boxHeight);
+ const font = await p5.loadFont(fonts[fontName]);
+ if (mode === '2d') p5.translate(p5.width / 2, p5.height / 2);
+ p5.textFont(font);
- p5.fill(0);
- p5.noStroke();
- p5.text(
- 'A really long text that should wrap automatically as it reaches the end of the box',
- xPos,
- yPos,
- boxWidth,
- boxHeight
- );
- const bb = p5.textBounds(
- 'A really long text that should wrap automatically as it reaches the end of the box',
- xPos,
- yPos,
- boxWidth,
- boxHeight
- );
- p5.noFill();
p5.stroke('red');
- p5.rect(bb.x, bb.y, bb.w, bb.h);
- p5.pop();
+ p5.line(-p5.width / 2, 0, p5.width / 2, 0);
+ p5.noStroke();
+ p5.fill(0);
+ p5.textSize(20);
+ p5.textAlign(p5.CENTER, p5.CENTER);
+ p5.text('Vertical align', 0, 0);
screenshot();
- });
- }
- );
-
- visualTest(
- 'all alignments with multi-lines and wrap char',
- async function (p5, screenshot) {
- const alignments = [
- { alignX: p5.LEFT, alignY: p5.TOP },
- { alignX: p5.CENTER, alignY: p5.TOP },
- { alignX: p5.RIGHT, alignY: p5.TOP },
- { alignX: p5.LEFT, alignY: p5.CENTER },
- { alignX: p5.CENTER, alignY: p5.CENTER },
- { alignX: p5.RIGHT, alignY: p5.CENTER },
- { alignX: p5.LEFT, alignY: p5.BOTTOM },
- { alignX: p5.CENTER, alignY: p5.BOTTOM },
- { alignX: p5.RIGHT, alignY: p5.BOTTOM }
- ];
-
- p5.createCanvas(150, 100, mode === 'webgl' ? p5.WEBGL : undefined);
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
- p5.textSize(19);
- p5.textWrap(p5.CHAR);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.textFont(font);
-
- let xPos = 20;
- let yPos = 20;
- const boxWidth = 100;
- const boxHeight = 60;
-
- alignments.forEach((alignment, i) => {
- p5.background(255);
- p5.push();
- p5.textAlign(alignment.alignX, alignment.alignY);
-
- p5.noFill();
- p5.strokeWeight(2);
- p5.stroke(200);
- p5.rect(xPos, yPos, boxWidth, boxHeight);
+ }
+ );
+ }
+ }
+ });
- p5.fill(0);
- p5.noStroke();
- p5.text(
- 'A really long text that should wrap automatically as it reaches the end of the box',
- xPos,
- yPos,
- boxWidth,
- boxHeight
+ visualSuite('textAlign', function () {
+ for (const mode of ['2d', 'webgl']) {
+ visualSuite(`${mode} mode`, () => {
+ visualTest(
+ 'all alignments with single word',
+ async function (p5, screenshot) {
+ const alignments = [
+ { alignX: p5.LEFT, alignY: p5.TOP },
+ { alignX: p5.CENTER, alignY: p5.TOP },
+ { alignX: p5.RIGHT, alignY: p5.TOP },
+ { alignX: p5.LEFT, alignY: p5.CENTER },
+ { alignX: p5.CENTER, alignY: p5.CENTER },
+ { alignX: p5.RIGHT, alignY: p5.CENTER },
+ { alignX: p5.LEFT, alignY: p5.BOTTOM },
+ { alignX: p5.CENTER, alignY: p5.BOTTOM },
+ { alignX: p5.RIGHT, alignY: p5.BOTTOM }
+ ];
+
+ p5.createCanvas(
+ 300,
+ 300,
+ mode === 'webgl' ? p5.WEBGL : undefined
);
- const bb = p5.textBounds(
- 'A really long text that should wrap automatically as it reaches the end of the box',
- xPos,
- yPos,
- boxWidth,
- boxHeight
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textSize(60);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
);
- p5.noFill();
- p5.stroke('red');
- p5.rect(bb.x, bb.y, bb.w, bb.h);
- p5.pop();
-
- screenshot();
- });
- }
- );
-
- visualTest(
- 'all alignments with multi-line manual text',
- async function (p5, screenshot) {
- const alignments = [
- { alignX: p5.LEFT, alignY: p5.TOP },
- { alignX: p5.CENTER, alignY: p5.TOP },
- { alignX: p5.RIGHT, alignY: p5.TOP },
- { alignX: p5.LEFT, alignY: p5.CENTER },
- { alignX: p5.CENTER, alignY: p5.CENTER },
- { alignX: p5.RIGHT, alignY: p5.CENTER },
- { alignX: p5.LEFT, alignY: p5.BOTTOM },
- { alignX: p5.CENTER, alignY: p5.BOTTOM },
- { alignX: p5.RIGHT, alignY: p5.BOTTOM }
- ];
-
- p5.createCanvas(150, 100, mode === 'webgl' ? p5.WEBGL : undefined);
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
- p5.textSize(20);
-
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.textFont(font);
-
- let xPos = 20;
- let yPos = 20;
- const boxWidth = 100;
- const boxHeight = 60;
-
- alignments.forEach((alignment, i) => {
- p5.background(255);
- p5.push();
- p5.textAlign(alignment.alignX, alignment.alignY);
-
- p5.noFill();
- p5.stroke(200);
- p5.strokeWeight(2);
- p5.rect(xPos, yPos, boxWidth, boxHeight);
+ p5.textFont(font);
+ alignments.forEach(alignment => {
+ p5.background(255);
+ p5.textAlign(alignment.alignX, alignment.alignY);
+ p5.text('Single Line', p5.width / 2, p5.height / 2);
+ const bb = p5.textBounds(
+ 'Single Line',
+ p5.width / 2,
+ p5.height / 2
+ );
+ p5.push();
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.pop();
+ screenshot();
+ });
+ }
+ );
- p5.fill(0);
- p5.noStroke();
- p5.text('Line 1\nLine 2\nLine 3', xPos, yPos, boxWidth, boxHeight);
- const bb = p5.textBounds(
- 'Line 1\nLine 2\nLine 3',
- xPos,
- yPos,
- boxWidth,
- boxHeight
+ visualTest(
+ 'all alignments with single line',
+ async function (p5, screenshot) {
+ const alignments = [
+ { alignX: p5.LEFT, alignY: p5.TOP },
+ { alignX: p5.CENTER, alignY: p5.TOP },
+ { alignX: p5.RIGHT, alignY: p5.TOP },
+ { alignX: p5.LEFT, alignY: p5.CENTER },
+ { alignX: p5.CENTER, alignY: p5.CENTER },
+ { alignX: p5.RIGHT, alignY: p5.CENTER },
+ { alignX: p5.LEFT, alignY: p5.BOTTOM },
+ { alignX: p5.CENTER, alignY: p5.BOTTOM },
+ { alignX: p5.RIGHT, alignY: p5.BOTTOM }
+ ];
+
+ p5.createCanvas(
+ 300,
+ 300,
+ mode === 'webgl' ? p5.WEBGL : undefined
);
- p5.noFill();
- p5.stroke('red');
- p5.rect(bb.x, bb.y, bb.w, bb.h);
- p5.pop();
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textSize(45);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.textFont(font);
+ alignments.forEach(alignment => {
+ p5.background(255);
+ p5.textAlign(alignment.alignX, alignment.alignY);
+ p5.text('Single Line', p5.width / 2, p5.height / 2);
+ const bb = p5.textBounds(
+ 'Single Line',
+ p5.width / 2,
+ p5.height / 2
+ );
+ p5.push();
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.pop();
+ screenshot();
+ });
+ }
+ );
- screenshot();
- });
- }
- );
+ visualTest(
+ 'all alignments with multi-lines and wrap word',
+ async function (p5, screenshot) {
+ const alignments = [
+ { alignX: p5.LEFT, alignY: p5.TOP },
+ { alignX: p5.CENTER, alignY: p5.TOP },
+ { alignX: p5.RIGHT, alignY: p5.TOP },
+ { alignX: p5.LEFT, alignY: p5.CENTER },
+ { alignX: p5.CENTER, alignY: p5.CENTER },
+ { alignX: p5.RIGHT, alignY: p5.CENTER },
+ { alignX: p5.LEFT, alignY: p5.BOTTOM },
+ { alignX: p5.CENTER, alignY: p5.BOTTOM },
+ { alignX: p5.RIGHT, alignY: p5.BOTTOM }
+ ];
+
+ p5.createCanvas(
+ 150,
+ 100,
+ mode === 'webgl' ? p5.WEBGL : undefined
+ );
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textSize(20);
+ p5.textWrap(p5.WORD);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.textFont(font);
+
+ let xPos = 20;
+ let yPos = 20;
+ const boxWidth = 100;
+ const boxHeight = 60;
+
+ alignments.forEach((alignment, i) => {
+ p5.background(255);
+ p5.push();
+ p5.textAlign(alignment.alignX, alignment.alignY);
+
+ p5.noFill();
+ p5.strokeWeight(2);
+ p5.stroke(200);
+ p5.rect(xPos, yPos, boxWidth, boxHeight);
+
+ p5.fill(0);
+ p5.noStroke();
+ p5.text(
+ 'A really long text that should wrap automatically as it reaches the end of the box',
+ xPos,
+ yPos,
+ boxWidth,
+ boxHeight
+ );
+ const bb = p5.textBounds(
+ 'A really long text that should wrap automatically as it reaches the end of the box',
+ xPos,
+ yPos,
+ boxWidth,
+ boxHeight
+ );
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.pop();
+
+ screenshot();
+ });
+ }
+ );
- visualTest(
- 'all alignments with multi-line manual text, no box dimensions',
- async function (p5, screenshot) {
- const alignments = [
- { alignX: p5.LEFT, alignY: p5.TOP },
- { alignX: p5.CENTER, alignY: p5.TOP },
- { alignX: p5.RIGHT, alignY: p5.TOP },
- { alignX: p5.LEFT, alignY: p5.CENTER },
- { alignX: p5.CENTER, alignY: p5.CENTER },
- { alignX: p5.RIGHT, alignY: p5.CENTER },
- { alignX: p5.LEFT, alignY: p5.BOTTOM },
- { alignX: p5.CENTER, alignY: p5.BOTTOM },
- { alignX: p5.RIGHT, alignY: p5.BOTTOM }
- ];
-
- p5.createCanvas(150, 100, mode === 'webgl' ? p5.WEBGL : undefined);
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
- p5.textSize(20);
+ visualTest(
+ 'all alignments with multi-lines and wrap char',
+ async function (p5, screenshot) {
+ const alignments = [
+ { alignX: p5.LEFT, alignY: p5.TOP },
+ { alignX: p5.CENTER, alignY: p5.TOP },
+ { alignX: p5.RIGHT, alignY: p5.TOP },
+ { alignX: p5.LEFT, alignY: p5.CENTER },
+ { alignX: p5.CENTER, alignY: p5.CENTER },
+ { alignX: p5.RIGHT, alignY: p5.CENTER },
+ { alignX: p5.LEFT, alignY: p5.BOTTOM },
+ { alignX: p5.CENTER, alignY: p5.BOTTOM },
+ { alignX: p5.RIGHT, alignY: p5.BOTTOM }
+ ];
+
+ p5.createCanvas(
+ 150,
+ 100,
+ mode === 'webgl' ? p5.WEBGL : undefined
+ );
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textSize(19);
+ p5.textWrap(p5.CHAR);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.textFont(font);
+
+ let xPos = 20;
+ let yPos = 20;
+ const boxWidth = 100;
+ const boxHeight = 60;
+
+ alignments.forEach((alignment, i) => {
+ p5.background(255);
+ p5.push();
+ p5.textAlign(alignment.alignX, alignment.alignY);
+
+ p5.noFill();
+ p5.strokeWeight(2);
+ p5.stroke(200);
+ p5.rect(xPos, yPos, boxWidth, boxHeight);
+
+ p5.fill(0);
+ p5.noStroke();
+ p5.text(
+ 'A really long text that should wrap automatically as it reaches the end of the box',
+ xPos,
+ yPos,
+ boxWidth,
+ boxHeight
+ );
+ const bb = p5.textBounds(
+ 'A really long text that should wrap automatically as it reaches the end of the box',
+ xPos,
+ yPos,
+ boxWidth,
+ boxHeight
+ );
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.pop();
+
+ screenshot();
+ });
+ }
+ );
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.textFont(font);
+ visualTest(
+ 'all alignments with multi-line manual text',
+ async function (p5, screenshot) {
+ const alignments = [
+ { alignX: p5.LEFT, alignY: p5.TOP },
+ { alignX: p5.CENTER, alignY: p5.TOP },
+ { alignX: p5.RIGHT, alignY: p5.TOP },
+ { alignX: p5.LEFT, alignY: p5.CENTER },
+ { alignX: p5.CENTER, alignY: p5.CENTER },
+ { alignX: p5.RIGHT, alignY: p5.CENTER },
+ { alignX: p5.LEFT, alignY: p5.BOTTOM },
+ { alignX: p5.CENTER, alignY: p5.BOTTOM },
+ { alignX: p5.RIGHT, alignY: p5.BOTTOM }
+ ];
+
+ p5.createCanvas(
+ 150,
+ 100,
+ mode === 'webgl' ? p5.WEBGL : undefined
+ );
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textSize(20);
- let xPos = 20;
- let yPos = 20;
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.textFont(font);
+
+ let xPos = 20;
+ let yPos = 20;
+ const boxWidth = 100;
+ const boxHeight = 60;
+
+ alignments.forEach((alignment, i) => {
+ p5.background(255);
+ p5.push();
+ p5.textAlign(alignment.alignX, alignment.alignY);
+
+ p5.noFill();
+ p5.stroke(200);
+ p5.strokeWeight(2);
+ p5.rect(xPos, yPos, boxWidth, boxHeight);
+
+ p5.fill(0);
+ p5.noStroke();
+ p5.text(
+ 'Line 1\nLine 2\nLine 3',
+ xPos,
+ yPos,
+ boxWidth,
+ boxHeight
+ );
+ const bb = p5.textBounds(
+ 'Line 1\nLine 2\nLine 3',
+ xPos,
+ yPos,
+ boxWidth,
+ boxHeight
+ );
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.pop();
+
+ screenshot();
+ });
+ }
+ );
- alignments.forEach((alignment, i) => {
- p5.background(255);
- p5.push();
- p5.textAlign(alignment.alignX, alignment.alignY);
+ visualTest(
+ 'all alignments with multi-line manual text, no box dimensions',
+ async function (p5, screenshot) {
+ const alignments = [
+ { alignX: p5.LEFT, alignY: p5.TOP },
+ { alignX: p5.CENTER, alignY: p5.TOP },
+ { alignX: p5.RIGHT, alignY: p5.TOP },
+ { alignX: p5.LEFT, alignY: p5.CENTER },
+ { alignX: p5.CENTER, alignY: p5.CENTER },
+ { alignX: p5.RIGHT, alignY: p5.CENTER },
+ { alignX: p5.LEFT, alignY: p5.BOTTOM },
+ { alignX: p5.CENTER, alignY: p5.BOTTOM },
+ { alignX: p5.RIGHT, alignY: p5.BOTTOM }
+ ];
+
+ p5.createCanvas(
+ 150,
+ 100,
+ mode === 'webgl' ? p5.WEBGL : undefined
+ );
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textSize(20);
- p5.fill(0);
- p5.noStroke();
- p5.text('Line 1\nLine 2\nLine 3', xPos, yPos);
- const bb = p5.textBounds(
- 'Line 1\nLine 2\nLine 3',
- xPos,
- yPos
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
);
- p5.noFill();
- p5.stroke('red');
- p5.rect(bb.x, bb.y, bb.w, bb.h);
- p5.pop();
+ p5.textFont(font);
+
+ let xPos = 20;
+ let yPos = 20;
+
+ alignments.forEach((alignment, i) => {
+ p5.background(255);
+ p5.push();
+ p5.textAlign(alignment.alignX, alignment.alignY);
+
+ p5.fill(0);
+ p5.noStroke();
+ p5.text('Line 1\nLine 2\nLine 3', xPos, yPos);
+ const bb = p5.textBounds('Line 1\nLine 2\nLine 3', xPos, yPos);
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.pop();
+
+ screenshot();
+ });
+ }
+ );
+ });
+ }
+ });
- screenshot();
- });
- }
+ visualSuite('textStyle', function () {
+ visualTest('all text styles', async function (p5, screenshot) {
+ p5.createCanvas(150, 150);
+ const font = await p5.loadFont(
+ 'https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&display=swap'
);
+ p5.textSize(20);
+ p5.textFont(font);
+ p5.textAlign(p5.LEFT, p5.TOP);
+
+ p5.text('Regular Text', 0, 0);
+ p5.textStyle(p5.BOLD);
+ p5.text('Bold Text', 0, 30);
+ p5.textStyle(p5.ITALIC);
+ p5.text('Italic Text', 0, 60);
+ p5.textStyle(p5.BOLDITALIC);
+ p5.text('Bold Italic Text', 0, 90);
+ screenshot();
});
- }
- });
-
- visualSuite('textStyle', function () {
- visualTest('all text styles', async function (p5, screenshot) {
- p5.createCanvas(150, 150);
- const font = await p5.loadFont(
- 'https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&display=swap'
- );
- p5.textSize(20);
- p5.textFont(font);
- p5.textAlign(p5.LEFT, p5.TOP);
-
- p5.text('Regular Text', 0, 0);
- p5.textStyle(p5.BOLD);
- p5.text('Bold Text', 0, 30);
- p5.textStyle(p5.ITALIC);
- p5.text('Italic Text', 0, 60);
- p5.textStyle(p5.BOLDITALIC);
- p5.text('Bold Italic Text', 0, 90);
- screenshot();
});
- });
- visualSuite('textSize', function () {
- const units = ['px'];
- const sizes = [12, 16, 20, 24, 30];
+ visualSuite('textSize', function () {
+ const units = ['px'];
+ const sizes = [12, 16, 20, 24, 30];
- visualTest('text sizes comparison', function (p5, screenshot) {
- p5.createCanvas(300, 200);
- let yOffset = 0;
+ visualTest('text sizes comparison', function (p5, screenshot) {
+ p5.createCanvas(300, 200);
+ let yOffset = 0;
- units.forEach(unit => {
- sizes.forEach(size => {
- p5.textSize(size);
- p5.textAlign(p5.LEFT, p5.TOP);
- p5.text(`Size: ${size}${unit}`, 0, yOffset);
- yOffset += 30;
+ units.forEach(unit => {
+ sizes.forEach(size => {
+ p5.textSize(size);
+ p5.textAlign(p5.LEFT, p5.TOP);
+ p5.text(`Size: ${size}${unit}`, 0, yOffset);
+ yOffset += 30;
+ });
});
+ screenshot();
});
- screenshot();
- });
- });
-
- visualSuite('textLeading', function () {
- visualTest('text leading with different values', function (p5, screenshot) {
- p5.createCanvas(300, 200);
- const leadingValues = [10, 20, 30];
- let yOffset = 0;
-
- p5.textSize(20);
- p5.textAlign(p5.LEFT, p5.TOP);
-
- leadingValues.forEach(leading => {
- p5.textLeading(leading);
- p5.text(`Leading: ${leading}`, 0, yOffset);
- p5.text('This is a line of text.', 0, yOffset + 30);
- p5.text('This is another line of text.', 0, yOffset + 30 + leading);
- yOffset += 30 + leading;
- });
- screenshot();
});
- });
-
- visualSuite('textWidth', function () {
- visualTest('verify width of a string', function (p5, screenshot) {
- p5.createCanvas(100, 100);
- p5.textSize(20);
- const text = 'Width Test';
- const width = p5.textWidth(text);
- p5.text(text, 0, 30);
- p5.noFill();
- p5.stroke('red');
- p5.rect(0, 30 - 20, width, 20);
- screenshot();
- });
- });
- visualSuite('textToPoints', function () {
- visualTest('Fonts can be converted to points', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.background(255);
- p5.strokeWeight(2);
- p5.textSize(50);
- const pts = font.textToPoints('p5*js', 0, 50);
- p5.beginShape(p5.POINTS);
- for (const { x, y } of pts) p5.vertex(x, y);
- p5.endShape();
- screenshot();
- });
+ visualSuite('textLeading', function () {
+ visualTest(
+ 'text leading with different values',
+ function (p5, screenshot) {
+ p5.createCanvas(300, 200);
+ const leadingValues = [10, 20, 30];
+ let yOffset = 0;
+
+ p5.textSize(20);
+ p5.textAlign(p5.LEFT, p5.TOP);
- visualTest('Sampling density can be changed', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
+ leadingValues.forEach(leading => {
+ p5.textLeading(leading);
+ p5.text(`Leading: ${leading}`, 0, yOffset);
+ p5.text('This is a line of text.', 0, yOffset + 30);
+ p5.text('This is another line of text.', 0, yOffset + 30 + leading);
+ yOffset += 30 + leading;
+ });
+ screenshot();
+ }
);
- p5.background(255);
- p5.strokeWeight(2);
- p5.textSize(50);
- const pts = font.textToPoints('p5*js', 0, 50, { sampleFactor: 0.5 });
- p5.beginShape(p5.POINTS);
- for (const { x, y } of pts) p5.vertex(x, y);
- p5.endShape();
- screenshot();
});
- for (const mode of ['RADIANS', 'DEGREES']) {
- visualTest(`Fonts point angles work in ${mode} mode`, async function (p5, screenshot) {
+ visualSuite('textWidth', function () {
+ visualTest('verify width of a string', function (p5, screenshot) {
p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.background(255);
- p5.strokeWeight(2);
- p5.textSize(50);
- p5.angleMode(p5[mode]);
- const pts = font.textToPoints('p5*js', 0, 50, { sampleFactor: 0.25 });
- p5.beginShape(p5.LINES);
- for (const { x, y, angle } of pts) {
- p5.vertex(
- x - 5 * p5.cos(angle),
- y - 5 * p5.sin(angle)
+ p5.textSize(20);
+ const text = 'Width Test';
+ const width = p5.textWidth(text);
+ p5.text(text, 0, 30);
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(0, 30 - 20, width, 20);
+ screenshot();
+ });
+ });
+
+ visualSuite('textToPoints', function () {
+ visualTest(
+ 'Fonts can be converted to points',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
);
- p5.vertex(
- x + 5 * p5.cos(angle),
- y + 5 * p5.sin(angle)
+ p5.background(255);
+ p5.strokeWeight(2);
+ p5.textSize(50);
+ const pts = font.textToPoints('p5*js', 0, 50);
+ p5.beginShape(p5.POINTS);
+ for (const { x, y } of pts) p5.vertex(x, y);
+ p5.endShape();
+ screenshot();
+ }
+ );
+
+ visualTest(
+ 'Sampling density can be changed',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
);
+ p5.background(255);
+ p5.strokeWeight(2);
+ p5.textSize(50);
+ const pts = font.textToPoints('p5*js', 0, 50, { sampleFactor: 0.5 });
+ p5.beginShape(p5.POINTS);
+ for (const { x, y } of pts) p5.vertex(x, y);
+ p5.endShape();
+ screenshot();
}
- p5.endShape();
- screenshot();
- });
- }
- });
-
- visualSuite('textToContours', function () {
- visualTest('Fonts can be converted to points grouped by contour', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
);
- p5.background(200);
- p5.strokeWeight(2);
- p5.textSize(50);
- const contours = font.textToContours('p5*js', 0, 50, { sampleFactor: 0.5 });
- p5.beginShape();
- for (const pts of contours) {
- p5.beginContour();
- for (const { x, y } of pts) p5.vertex(x, y);
- p5.endContour(p5.CLOSE);
+
+ for (const mode of ['RADIANS', 'DEGREES']) {
+ visualTest(
+ `Fonts point angles work in ${mode} mode`,
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.background(255);
+ p5.strokeWeight(2);
+ p5.textSize(50);
+ p5.angleMode(p5[mode]);
+ const pts = font.textToPoints('p5*js', 0, 50, {
+ sampleFactor: 0.25
+ });
+ p5.beginShape(p5.LINES);
+ for (const { x, y, angle } of pts) {
+ p5.vertex(x - 5 * p5.cos(angle), y - 5 * p5.sin(angle));
+ p5.vertex(x + 5 * p5.cos(angle), y + 5 * p5.sin(angle));
+ }
+ p5.endShape();
+ screenshot();
+ }
+ );
}
- p5.endShape();
- screenshot();
});
- });
- visualSuite('textToPaths', function () {
- visualTest('Fonts can be converted to drawing context commands', async function (p5, screenshot) {
- p5.createCanvas(100, 100);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
+ visualSuite('textToContours', function () {
+ visualTest(
+ 'Fonts can be converted to points grouped by contour',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.background(200);
+ p5.strokeWeight(2);
+ p5.textSize(50);
+ const contours = font.textToContours('p5*js', 0, 50, {
+ sampleFactor: 0.5
+ });
+ p5.beginShape();
+ for (const pts of contours) {
+ p5.beginContour();
+ for (const { x, y } of pts) p5.vertex(x, y);
+ p5.endContour(p5.CLOSE);
+ }
+ p5.endShape();
+ screenshot();
+ }
);
- p5.background(200);
- p5.strokeWeight(2);
- p5.textSize(50);
- const cmds = font.textToPaths('p5*js', 0, 50);
- p5.drawingContext.beginPath();
- for (const [type, ...args] of cmds) {
- if (type === 'M') {
- p5.drawingContext.moveTo(...args);
- } else if (type === 'L') {
- p5.drawingContext.lineTo(...args);
- } else if (type === 'C') {
- p5.drawingContext.bezierCurveTo(...args);
- } else if (type === 'Q') {
- p5.drawingContext.quadraticCurveTo(...args);
- } else if (type === 'Z') {
- p5.drawingContext.closePath();
+ });
+
+ visualSuite('textToPaths', function () {
+ visualTest(
+ 'Fonts can be converted to drawing context commands',
+ async function (p5, screenshot) {
+ p5.createCanvas(100, 100);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.background(200);
+ p5.strokeWeight(2);
+ p5.textSize(50);
+ const cmds = font.textToPaths('p5*js', 0, 50);
+ p5.drawingContext.beginPath();
+ for (const [type, ...args] of cmds) {
+ if (type === 'M') {
+ p5.drawingContext.moveTo(...args);
+ } else if (type === 'L') {
+ p5.drawingContext.lineTo(...args);
+ } else if (type === 'C') {
+ p5.drawingContext.bezierCurveTo(...args);
+ } else if (type === 'Q') {
+ p5.drawingContext.quadraticCurveTo(...args);
+ } else if (type === 'Z') {
+ p5.drawingContext.closePath();
+ }
+ }
+ p5.drawingContext.fill();
+ p5.drawingContext.stroke();
+ screenshot();
}
- }
- p5.drawingContext.fill();
- p5.drawingContext.stroke();
- screenshot();
+ );
});
- });
-}, { shiftThreshold: 3 });
+ },
+ { shiftThreshold: 3 }
+);
diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js
index 16d99139a1..ed2af5fdc6 100644
--- a/test/unit/visual/cases/webgl.js
+++ b/test/unit/visual/cases/webgl.js
@@ -1,80 +1,88 @@
import { vi, afterEach } from 'vitest';
import { visualSuite, visualTest } from '../visualTest';
-visualSuite('WebGL', function() {
- visualSuite('Camera', function() {
- visualTest('2D objects maintain correct size', function(p5, screenshot) {
+visualSuite('WebGL', function () {
+ visualSuite('Camera', function () {
+ visualTest('2D objects maintain correct size', function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
p5.noStroke();
p5.fill('red');
p5.rectMode(p5.CENTER);
- p5.rect(0, 0, p5.width/2, p5.height/2);
+ p5.rect(0, 0, p5.width / 2, p5.height / 2);
screenshot();
});
- visualTest('Custom camera before and after resize', function(p5, screenshot) {
- p5.createCanvas(25, 50, p5.WEBGL);
- const cam = p5.createCamera();
- p5.setCamera(cam);
- cam.setPosition(-10, -10, 800);
- p5.strokeWeight(4);
- p5.box(20);
- screenshot();
+ visualTest(
+ 'Custom camera before and after resize',
+ function (p5, screenshot) {
+ p5.createCanvas(25, 50, p5.WEBGL);
+ const cam = p5.createCamera();
+ p5.setCamera(cam);
+ cam.setPosition(-10, -10, 800);
+ p5.strokeWeight(4);
+ p5.box(20);
+ screenshot();
- p5.resizeCanvas(50, 25);
- p5.box(20);
- screenshot();
- });
+ p5.resizeCanvas(50, 25);
+ p5.box(20);
+ screenshot();
+ }
+ );
- visualTest('Camera settings on framebuffers reset after push/pop', function(p5, screenshot) {
- p5.createCanvas(100, 100, p5.WEBGL);
- p5.setAttributes({ antialias: true });
- const fbo = p5.createFramebuffer();
+ visualTest(
+ 'Camera settings on framebuffers reset after push/pop',
+ function (p5, screenshot) {
+ p5.createCanvas(100, 100, p5.WEBGL);
+ p5.setAttributes({ antialias: true });
+ const fbo = p5.createFramebuffer();
- p5.background(220);
- p5.imageMode(p5.CENTER);
+ p5.background(220);
+ p5.imageMode(p5.CENTER);
- fbo.begin();
- p5.push();
- p5.ortho();
- p5.translate(0, -25);
- for (let i = -1; i <= 1; i++) {
+ fbo.begin();
p5.push();
- p5.translate(i * 35, 0);
- p5.box(25, 25, 150);
+ p5.ortho();
+ p5.translate(0, -25);
+ for (let i = -1; i <= 1; i++) {
+ p5.push();
+ p5.translate(i * 35, 0);
+ p5.box(25, 25, 150);
+ p5.pop();
+ }
p5.pop();
- }
- p5.pop();
-
- p5.push();
- p5.translate(0, 25);
- for (let i = -1; i <= 1; i++) {
p5.push();
- p5.translate(i * 35, 0);
- p5.box(25, 25, 150);
+ p5.translate(0, 25);
+ for (let i = -1; i <= 1; i++) {
+ p5.push();
+ p5.translate(i * 35, 0);
+ p5.box(25, 25, 150);
+ p5.pop();
+ }
p5.pop();
- }
- p5.pop();
- fbo.end();
- p5.image(fbo, 0, 0);
- screenshot();
- });
+ fbo.end();
+ p5.image(fbo, 0, 0);
+ screenshot();
+ }
+ );
- visualTest('Works after perspective() with no args', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(200);
- p5.perspective();
- p5.noStroke();
- p5.lights();
- p5.sphere(20);
- screenshot();
- });
+ visualTest(
+ 'Works after perspective() with no args',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(200);
+ p5.perspective();
+ p5.noStroke();
+ p5.lights();
+ p5.sphere(20);
+ screenshot();
+ }
+ );
});
- visualSuite('filter', function() {
- visualTest('On the main canvas', function(p5, screenshot) {
+ visualSuite('filter', function () {
+ visualTest('On the main canvas', function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
p5.noStroke();
p5.fill('red');
@@ -83,7 +91,7 @@ visualSuite('WebGL', function() {
screenshot();
});
- visualTest('On a framebuffer', function(p5, screenshot) {
+ visualTest('On a framebuffer', function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
const fbo = p5.createFramebuffer({ antialias: true });
fbo.begin();
@@ -97,23 +105,30 @@ visualSuite('WebGL', function() {
screenshot();
});
- visualTest('On a framebuffer of a different size from the canvas', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- const fbo = p5.createFramebuffer({ antialias: true, width: 25, height: 100 });
- fbo.begin();
- p5.background('blue');
- p5.fill('red');
- p5.circle(0, 0, 20);
- p5.filter(p5.BLUR, 3);
- fbo.end();
- p5.imageMode(p5.CENTER);
- p5.image(fbo, 0, 0);
- screenshot();
- });
+ visualTest(
+ 'On a framebuffer of a different size from the canvas',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const fbo = p5.createFramebuffer({
+ antialias: true,
+ width: 25,
+ height: 100
+ });
+ fbo.begin();
+ p5.background('blue');
+ p5.fill('red');
+ p5.circle(0, 0, 20);
+ p5.filter(p5.BLUR, 3);
+ fbo.end();
+ p5.imageMode(p5.CENTER);
+ p5.image(fbo, 0, 0);
+ screenshot();
+ }
+ );
visualTest(
'On a framebuffer sized differently from the main canvas',
- function(p5, screenshot) {
+ function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
const fbo = p5.createFramebuffer({
width: 26,
@@ -133,10 +148,10 @@ visualSuite('WebGL', function() {
);
for (const mode of ['webgl', '2d']) {
- visualSuite(`In ${mode} mode`, function() {
- const setupSketch = (p5) => {
+ visualSuite(`In ${mode} mode`, function () {
+ const setupSketch = p5 => {
p5.createCanvas(50, 50, mode === 'webgl' ? p5.WEBGL : p5.P2D);
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
p5.clear();
p5.noStroke();
p5.fill('red');
@@ -161,52 +176,58 @@ visualSuite('WebGL', function() {
}
};
- visualTest('It can apply GRAY', function(p5, screenshot) {
+ visualTest('It can apply GRAY', function (p5, screenshot) {
setupSketch(p5);
p5.filter(p5.GRAY);
screenshot();
});
- visualTest('It can apply INVERT', function(p5, screenshot) {
+ visualTest('It can apply INVERT', function (p5, screenshot) {
setupSketch(p5);
p5.filter(p5.INVERT);
screenshot();
});
- visualTest('It can apply THRESHOLD', function(p5, screenshot) {
+ visualTest('It can apply THRESHOLD', function (p5, screenshot) {
setupSketch(p5);
p5.filter(p5.THRESHOLD);
screenshot();
});
- visualTest('It can apply THRESHOLD with a value', function(p5, screenshot) {
- setupSketch(p5);
- p5.filter(p5.THRESHOLD, 0.8);
- screenshot();
- });
- visualTest('It can apply POSTERIZE', function(p5, screenshot) {
+ visualTest(
+ 'It can apply THRESHOLD with a value',
+ function (p5, screenshot) {
+ setupSketch(p5);
+ p5.filter(p5.THRESHOLD, 0.8);
+ screenshot();
+ }
+ );
+ visualTest('It can apply POSTERIZE', function (p5, screenshot) {
setupSketch(p5);
p5.filter(p5.THRESHOLD);
screenshot();
});
- visualTest('It can apply POSTERIZE with a value', function(p5, screenshot) {
- setupSketch(p5);
- p5.filter(p5.THRESHOLD, 2);
- screenshot();
- });
- visualTest('It can apply BLUR', function(p5, screenshot) {
+ visualTest(
+ 'It can apply POSTERIZE with a value',
+ function (p5, screenshot) {
+ setupSketch(p5);
+ p5.filter(p5.THRESHOLD, 2);
+ screenshot();
+ }
+ );
+ visualTest('It can apply BLUR', function (p5, screenshot) {
setupSketch(p5);
p5.filter(p5.BLUR, 5);
screenshot();
});
- visualTest('It can apply BLUR with a value', function(p5, screenshot) {
+ visualTest('It can apply BLUR with a value', function (p5, screenshot) {
setupSketch(p5);
p5.filter(p5.BLUR, 10);
screenshot();
});
- visualTest('It can apply ERODE (4x)', function(p5, screenshot) {
+ visualTest('It can apply ERODE (4x)', function (p5, screenshot) {
setupSketch(p5);
for (let i = 0; i < 4; i++) p5.filter(p5.ERODE);
screenshot();
});
- visualTest('It can apply DILATE (4x)', function(p5, screenshot) {
+ visualTest('It can apply DILATE (4x)', function (p5, screenshot) {
setupSketch(p5);
for (let i = 0; i < 4; i++) p5.filter(p5.DILATE);
screenshot();
@@ -215,8 +236,8 @@ visualSuite('WebGL', function() {
}
for (const mode of ['webgl', '2d']) {
- visualSuite(`In ${mode} mode`, function() {
- visualTest('It can use filter shader hooks', function(p5, screenshot) {
+ visualSuite(`In ${mode} mode`, function () {
+ visualTest('It can use filter shader hooks', function (p5, screenshot) {
p5.createCanvas(50, 50, mode === 'webgl' ? p5.WEBGL : p5.P2D);
const s = p5.baseFilterShader().modify({
@@ -227,7 +248,7 @@ visualSuite('WebGL', function() {
}`
});
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
p5.background(255);
p5.fill('red');
p5.noStroke();
@@ -240,23 +261,26 @@ visualSuite('WebGL', function() {
}
for (const mode of ['webgl', '2d']) {
- visualSuite(`In ${mode} mode`, function() {
- visualTest('It can combine multiple filter passes', function(p5, screenshot) {
- p5.createCanvas(50, 50, mode === 'webgl' ? p5.WEBGL : p5.P2D);
- if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2);
- p5.background(255);
- p5.fill(0);
- p5.noStroke();
- p5.circle(15, 15, 20);
- p5.circle(30, 30, 20);
- p5.filter(p5.BLUR, 5);
- p5.filter(p5.THRESHOLD);
- screenshot();
- });
+ visualSuite(`In ${mode} mode`, function () {
+ visualTest(
+ 'It can combine multiple filter passes',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, mode === 'webgl' ? p5.WEBGL : p5.P2D);
+ if (mode === 'webgl') p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.background(255);
+ p5.fill(0);
+ p5.noStroke();
+ p5.circle(15, 15, 20);
+ p5.circle(30, 30, 20);
+ p5.filter(p5.BLUR, 5);
+ p5.filter(p5.THRESHOLD);
+ screenshot();
+ }
+ );
});
}
- visualTest('On a createGraphics WEBGL buffer', function(p5, screenshot) {
+ visualTest('On a createGraphics WEBGL buffer', function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
const g = p5.createGraphics(50, 50, p5.WEBGL);
@@ -274,97 +298,122 @@ visualSuite('WebGL', function() {
});
for (const mode of ['webgl', '2d']) {
- visualTest(`Transparent background colors are correct in ${mode} mode`, function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- const g = p5.createGraphics(50, 50, mode === 'webgl' ? p5.WEBGL : p5.P2D);
- if (mode === 'webgl') g.translate(-p5.width/2, -p5.height/2);
- g.noStroke();
- g.fill(255, 0, 0, 100);
- g.rect(10, 10, 30, 30);
- g.filter(p5.BLUR, 4);
- p5.imageMode(p5.CENTER);
- p5.image(g, 0, 0);
- screenshot();
- });
+ visualTest(
+ `Transparent background colors are correct in ${mode} mode`,
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const g = p5.createGraphics(
+ 50,
+ 50,
+ mode === 'webgl' ? p5.WEBGL : p5.P2D
+ );
+ if (mode === 'webgl') g.translate(-p5.width / 2, -p5.height / 2);
+ g.noStroke();
+ g.fill(255, 0, 0, 100);
+ g.rect(10, 10, 30, 30);
+ g.filter(p5.BLUR, 4);
+ p5.imageMode(p5.CENTER);
+ p5.image(g, 0, 0);
+ screenshot();
+ }
+ );
- visualTest(`Multiple filter passes work correctly on a p5.Graphics in ${mode} mode`, function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- const g = p5.createGraphics(50, 50, mode === 'webgl' ? p5.WEBGL : p5.P2D);
- if (mode === 'webgl') g.translate(-g.width/2, -g.height/2);
- g.background(255);
- g.noStroke();
- g.fill(0);
- g.rect(10, 10, 6, 6);
- g.filter(p5.BLUR, 2);
- g.rect(30, 30, 6, 6);
- g.filter(p5.BLUR, 2);
- p5.imageMode(p5.CENTER);
- p5.image(g, 0, 0);
- screenshot();
- });
+ visualTest(
+ `Multiple filter passes work correctly on a p5.Graphics in ${mode} mode`,
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const g = p5.createGraphics(
+ 50,
+ 50,
+ mode === 'webgl' ? p5.WEBGL : p5.P2D
+ );
+ if (mode === 'webgl') g.translate(-g.width / 2, -g.height / 2);
+ g.background(255);
+ g.noStroke();
+ g.fill(0);
+ g.rect(10, 10, 6, 6);
+ g.filter(p5.BLUR, 2);
+ g.rect(30, 30, 6, 6);
+ g.filter(p5.BLUR, 2);
+ p5.imageMode(p5.CENTER);
+ p5.image(g, 0, 0);
+ screenshot();
+ }
+ );
}
});
- visualSuite('Lights', function() {
- visualTest('Fill color and default ambient material', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.noStroke();
- p5.lights();
- p5.fill('red');
- p5.sphere(20);
- screenshot();
- });
+ visualSuite('Lights', function () {
+ visualTest(
+ 'Fill color and default ambient material',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.noStroke();
+ p5.lights();
+ p5.fill('red');
+ p5.sphere(20);
+ screenshot();
+ }
+ );
});
- visualSuite('3DModel', function() {
- visualTest('OBJ model with MTL file displays diffuse colors correctly', function(p5, screenshot) {
- return new Promise(resolve => {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.loadModel('test/unit/assets/octa-color.obj', model => {
- p5.background(255);
- p5.rotateX(10 * 0.01);
- p5.rotateY(10 * 0.01);
- model.normalize();
- p5.model(model);
- screenshot();
- resolve();
+ visualSuite('3DModel', function () {
+ visualTest(
+ 'OBJ model with MTL file displays diffuse colors correctly',
+ function (p5, screenshot) {
+ return new Promise(resolve => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.loadModel('test/unit/assets/octa-color.obj', model => {
+ p5.background(255);
+ p5.rotateX(10 * 0.01);
+ p5.rotateY(10 * 0.01);
+ model.normalize();
+ p5.model(model);
+ screenshot();
+ resolve();
+ });
});
- });
- });
- visualTest('Object with no colors takes on fill color', function(p5, screenshot) {
- return new Promise(resolve => {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.loadModel('test/unit/assets/cube.obj', model => {
- p5.background(255);
- p5.fill('blue'); // Setting a fill color
- p5.rotateX(p5.frameCount * 0.01);
- p5.rotateY(p5.frameCount * 0.01);
- model.normalize();
- p5.model(model);
- screenshot();
- resolve();
+ }
+ );
+ visualTest(
+ 'Object with no colors takes on fill color',
+ function (p5, screenshot) {
+ return new Promise(resolve => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.loadModel('test/unit/assets/cube.obj', model => {
+ p5.background(255);
+ p5.fill('blue'); // Setting a fill color
+ p5.rotateX(p5.frameCount * 0.01);
+ p5.rotateY(p5.frameCount * 0.01);
+ model.normalize();
+ p5.model(model);
+ screenshot();
+ resolve();
+ });
});
- });
- });
+ }
+ );
visualTest(
'Object with different texture coordinates per use of vertex keeps the coordinates intact',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
const tex = await p5.loadImage('test/unit/assets/cat.jpg');
- const cube = await new Promise(resolve => p5.loadModel('test/unit/assets/cube-textures.obj', resolve));
+ const cube = await new Promise(resolve =>
+ p5.loadModel('test/unit/assets/cube-textures.obj', resolve)
+ );
cube.normalize();
p5.background(255);
p5.texture(tex);
p5.rotateX(p5.PI / 4);
p5.rotateY(p5.PI / 4);
- p5.scale(80/400);
+ p5.scale(80 / 400);
p5.model(cube);
screenshot();
}
);
});
- visualSuite('vertexProperty', function(){
+ visualSuite('vertexProperty', function () {
const vertSrc = `#version 300 es
precision mediump float;
uniform mat4 uProjectionMatrix;
@@ -383,74 +432,71 @@ visualSuite('WebGL', function() {
void main(){
outColor = vec4(vCol, 1.0);
}`;
- visualTest(
- 'on PATH shape mode', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background('white');
- const myShader = p5.createShader(vertSrc, fragSrc);
- p5.shader(myShader);
- p5.beginShape(p5.PATH);
- p5.noStroke();
- for (let i = 0; i < 20; i++){
- let x = 20 * p5.sin(i/20*p5.TWO_PI);
- let y = 20 * p5.cos(i/20*p5.TWO_PI);
- p5.vertexProperty('aCol', [x/20, -y/20, 0]);
- p5.vertex(x, y);
- }
- p5.endShape();
- screenshot();
+ visualTest('on PATH shape mode', function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background('white');
+ const myShader = p5.createShader(vertSrc, fragSrc);
+ p5.shader(myShader);
+ p5.beginShape(p5.PATH);
+ p5.noStroke();
+ for (let i = 0; i < 20; i++) {
+ let x = 20 * p5.sin((i / 20) * p5.TWO_PI);
+ let y = 20 * p5.cos((i / 20) * p5.TWO_PI);
+ p5.vertexProperty('aCol', [x / 20, -y / 20, 0]);
+ p5.vertex(x, y);
}
- );
- visualTest(
- 'on QUADS shape mode', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background('white');
- const myShader = p5.createShader(vertSrc, fragSrc);
- p5.shader(myShader);
- p5.beginShape(p5.QUADS);
- p5.noStroke();
- p5.translate(-25,-25);
- for (let i = 0; i < 5; i++){
- for (let j = 0; j < 5; j++){
- let x1 = i * 10;
- let x2 = x1 + 10;
- let y1 = j * 10;
- let y2 = y1 + 10;
- p5.vertexProperty('aCol', [1, 0, 0]);
- p5.vertex(x1, y1);
- p5.vertexProperty('aCol', [0, 0, 1]);
- p5.vertex(x2, y1);
- p5.vertexProperty('aCol', [0, 1, 1]);
- p5.vertex(x2, y2);
- p5.vertexProperty('aCol', [1, 1, 1]);
- p5.vertex(x1, y2);
- }
+ p5.endShape();
+ screenshot();
+ });
+ visualTest('on QUADS shape mode', function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background('white');
+ const myShader = p5.createShader(vertSrc, fragSrc);
+ p5.shader(myShader);
+ p5.beginShape(p5.QUADS);
+ p5.noStroke();
+ p5.translate(-25, -25);
+ for (let i = 0; i < 5; i++) {
+ for (let j = 0; j < 5; j++) {
+ let x1 = i * 10;
+ let x2 = x1 + 10;
+ let y1 = j * 10;
+ let y2 = y1 + 10;
+ p5.vertexProperty('aCol', [1, 0, 0]);
+ p5.vertex(x1, y1);
+ p5.vertexProperty('aCol', [0, 0, 1]);
+ p5.vertex(x2, y1);
+ p5.vertexProperty('aCol', [0, 1, 1]);
+ p5.vertex(x2, y2);
+ p5.vertexProperty('aCol', [1, 1, 1]);
+ p5.vertex(x1, y2);
}
- p5.endShape();
- screenshot();
}
- );
+ p5.endShape();
+ screenshot();
+ });
visualTest(
- 'on buildGeometry outputs containing 3D primitives', function(p5, screenshot) {
+ 'on buildGeometry outputs containing 3D primitives',
+ function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
p5.background('white');
const myShader = p5.createShader(vertSrc, fragSrc);
p5.shader(myShader);
const shape = p5.buildGeometry(() => {
p5.push();
- p5.translate(15,-10,0);
+ p5.translate(15, -10, 0);
p5.sphere(5);
p5.pop();
p5.beginShape(p5.TRIANGLES);
- p5.vertexProperty('aCol', [1,0,0]);
+ p5.vertexProperty('aCol', [1, 0, 0]);
p5.vertex(-5, 5, 0);
- p5.vertexProperty('aCol', [0,1,0]);
+ p5.vertexProperty('aCol', [0, 1, 0]);
p5.vertex(5, 5, 0);
- p5.vertexProperty('aCol', [0,0,1]);
+ p5.vertexProperty('aCol', [0, 0, 1]);
p5.vertex(0, -5, 0);
p5.endShape(p5.CLOSE);
p5.push();
- p5.translate(-15,10,0);
+ p5.translate(-15, 10, 0);
p5.box(10);
p5.pop();
});
@@ -460,7 +506,7 @@ visualSuite('WebGL', function() {
);
});
- visualSuite('ShaderFunctionality', function() {
+ visualSuite('ShaderFunctionality', function () {
visualTest('FillShader', async (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
const img = await p5.loadImage('test/unit/assets/cat.jpg');
@@ -505,9 +551,9 @@ visualSuite('WebGL', function() {
p5.strokeWeight(15);
p5.line(
-p5.width / 3,
- p5.sin(0.2) * p5.height / 4,
+ (p5.sin(0.2) * p5.height) / 4,
p5.width / 3,
- p5.sin(1.2) * p5.height / 4
+ (p5.sin(1.2) * p5.height) / 4
);
screenshot();
});
@@ -550,7 +596,9 @@ visualSuite('WebGL', function() {
visualTest('loadMaterialShader', async (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const materialShader = await p5.loadMaterialShader('/test/unit/assets/testMaterial.js');
+ const materialShader = await p5.loadMaterialShader(
+ '/test/unit/assets/testMaterial.js'
+ );
p5.noStroke();
p5.shader(materialShader);
@@ -570,94 +618,121 @@ visualSuite('WebGL', function() {
} else {
p5.fill(0, 255, 0); // Green
}
- p5.rect(-p5.width/2 + i * 10, -p5.height/2, 10, p5.height);
+ p5.rect(-p5.width / 2 + i * 10, -p5.height / 2, 10, p5.height);
}
// Apply the filter shader (should swap red and green channels)
- const filterShader = await p5.loadFilterShader('/test/unit/assets/testFilter.js');
+ const filterShader = await p5.loadFilterShader(
+ '/test/unit/assets/testFilter.js'
+ );
p5.filter(filterShader);
screenshot();
});
});
- visualSuite('Strokes', function() {
- visualTest('Strokes do not cut into fills in ortho mode', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(220);
- p5.stroke(8);
- p5.ortho();
- p5.rotateX(p5.PI/4);
- p5.rotateY(p5.PI/4);
- p5.box(30);
- screenshot();
- });
+ visualSuite('Strokes', function () {
+ visualTest(
+ 'Strokes do not cut into fills in ortho mode',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(220);
+ p5.stroke(8);
+ p5.ortho();
+ p5.rotateX(p5.PI / 4);
+ p5.rotateY(p5.PI / 4);
+ p5.box(30);
+ screenshot();
+ }
+ );
});
- visualSuite('Opacity', function() {
- visualTest('Basic colors have opacity applied correctly', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(255);
- p5.fill(255, 100, 100, 100);
- p5.circle(0, 0, 50);
- screenshot();
- });
+ visualSuite('Opacity', function () {
+ visualTest(
+ 'Basic colors have opacity applied correctly',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(255);
+ p5.fill(255, 100, 100, 100);
+ p5.circle(0, 0, 50);
+ screenshot();
+ }
+ );
- visualTest('Colors have opacity applied correctly when lights are used', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(255);
- p5.ambientLight(255);
- p5.fill(255, 100, 100, 100);
- p5.circle(0, 0, 50);
- screenshot();
- });
+ visualTest(
+ 'Colors have opacity applied correctly when lights are used',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(255);
+ p5.ambientLight(255);
+ p5.fill(255, 100, 100, 100);
+ p5.circle(0, 0, 50);
+ screenshot();
+ }
+ );
- visualTest('Colors in shader hooks have opacity applied correctly', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const myShader = p5.baseMaterialShader().modify({
- 'Inputs getPixelInputs': `(Inputs inputs) {
+ visualTest(
+ 'Colors in shader hooks have opacity applied correctly',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const myShader = p5.baseMaterialShader().modify({
+ 'Inputs getPixelInputs': `(Inputs inputs) {
inputs.color = vec4(1., 0.4, 0.4, 100./255.);
return inputs;
}`
- });
- p5.background(255);
- p5.shader(myShader);
- p5.circle(0, 0, 50);
- screenshot();
- });
+ });
+ p5.background(255);
+ p5.shader(myShader);
+ p5.circle(0, 0, 50);
+ screenshot();
+ }
+ );
- visualTest('Colors in textures have opacity applied correctly', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const tex = p5.createFramebuffer();
- tex.draw(() => p5.background(255, 100, 100, 100));
- p5.background(255);
- p5.texture(tex);
- p5.circle(0, 0, 50);
- screenshot();
- });
+ visualTest(
+ 'Colors in textures have opacity applied correctly',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const tex = p5.createFramebuffer();
+ tex.draw(() => p5.background(255, 100, 100, 100));
+ p5.background(255);
+ p5.texture(tex);
+ p5.circle(0, 0, 50);
+ screenshot();
+ }
+ );
- visualTest('Colors in tinted textures have opacity applied correctly', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const tex = p5.createFramebuffer();
- tex.draw(() => p5.background(255, 100, 100, 255));
- p5.background(255);
- p5.texture(tex);
- p5.tint(255, 100);
- p5.circle(0, 0, 50);
- screenshot();
- });
+ visualTest(
+ 'Colors in tinted textures have opacity applied correctly',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const tex = p5.createFramebuffer();
+ tex.draw(() => p5.background(255, 100, 100, 255));
+ p5.background(255);
+ p5.texture(tex);
+ p5.tint(255, 100);
+ p5.circle(0, 0, 50);
+ screenshot();
+ }
+ );
- visualTest('noTint() before image() does not throw', async (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const img = await p5.loadImage('/test/unit/assets/cat.jpg');
- p5.noTint();
- p5.imageMode(p5.CENTER);
- p5.image(img, 0, 0, 50, 50);
- screenshot();
- });
+ visualTest(
+ 'noTint() before image() does not throw',
+ async (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const img = await p5.loadImage('/test/unit/assets/cat.jpg');
+ p5.noTint();
+ p5.imageMode(p5.CENTER);
+ p5.image(img, 0, 0, 50, 50);
+ screenshot();
+ }
+ );
});
visualSuite('Hooks coordinate spaces', () => {
- for (const base of ['baseMaterialShader', 'baseColorShader', 'baseNormalShader']) {
+ for (const base of [
+ 'baseMaterialShader',
+ 'baseColorShader',
+ 'baseNormalShader'
+ ]) {
visualSuite(base, () => {
visualTest('Object space', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
@@ -673,7 +748,7 @@ visualSuite('WebGL', function() {
p5.lights();
p5.fill('red');
p5.noStroke();
- p5.rotateY(p5.PI/2);
+ p5.rotateY(p5.PI / 2);
p5.camera(-800, 0, 0, 0, 0, 0);
p5.shader(myShader);
p5.sphere(20);
@@ -694,7 +769,7 @@ visualSuite('WebGL', function() {
p5.lights();
p5.fill('red');
p5.noStroke();
- p5.rotateY(p5.PI/2);
+ p5.rotateY(p5.PI / 2);
p5.camera(-800, 0, 0, 0, 0, 0);
p5.shader(myShader);
p5.sphere(20);
@@ -715,7 +790,7 @@ visualSuite('WebGL', function() {
p5.lights();
p5.fill('red');
p5.noStroke();
- p5.rotateY(p5.PI/2);
+ p5.rotateY(p5.PI / 2);
p5.camera(-800, 0, 0, 0, 0, 0);
p5.shader(myShader);
p5.sphere(20);
@@ -752,9 +827,7 @@ visualSuite('WebGL', function() {
visualSuite('textToModel', () => {
visualTest('Flat', async (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
+ const font = await p5.loadFont('test/unit/assets/Inconsolata-Bold.ttf');
p5.textSize(20);
const geom = font.textToModel('p5*js', 0, 0, {
sampleFactor: 2
@@ -762,18 +835,16 @@ visualSuite('WebGL', function() {
geom.normalize();
p5.background(255);
p5.normalMaterial();
- p5.rotateX(p5.PI*0.1);
- p5.rotateY(p5.PI*0.1);
- p5.scale(50/200);
+ p5.rotateX(p5.PI * 0.1);
+ p5.rotateY(p5.PI * 0.1);
+ p5.scale(50 / 200);
p5.model(geom);
screenshot();
});
visualTest('Extruded', async (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
+ const font = await p5.loadFont('test/unit/assets/Inconsolata-Bold.ttf');
p5.textSize(20);
const geom = font.textToModel('p5*js', 0, 0, {
extrude: 10,
@@ -782,9 +853,9 @@ visualSuite('WebGL', function() {
geom.normalize();
p5.background(255);
p5.normalMaterial();
- p5.rotateX(p5.PI*0.1);
- p5.rotateY(p5.PI*0.1);
- p5.scale(50/200);
+ p5.rotateX(p5.PI * 0.1);
+ p5.rotateY(p5.PI * 0.1);
+ p5.scale(50 / 200);
p5.model(geom);
screenshot();
});
@@ -843,14 +914,14 @@ visualSuite('WebGL', function() {
const geom = p5.buildGeometry(() => {
p5.push();
- p5.translate(-p5.width*0.2, 0);
+ p5.translate(-p5.width * 0.2, 0);
p5.scale(0.15);
p5.sphere();
p5.pop();
p5.push();
p5.fill('red');
- p5.translate(p5.width*0.2, 0);
+ p5.translate(p5.width * 0.2, 0);
p5.scale(0.15);
p5.sphere();
p5.pop();
@@ -868,53 +939,55 @@ visualSuite('WebGL', function() {
vi.restoreAllMocks();
});
- visualTest('glyph resource allocation does not corrupt textures', async (p5, screenshot) => {
- p5.createCanvas(100, 100, p5.WEBGL);
- vi.spyOn(p5._renderer, 'maxCachedGlyphs').mockReturnValue(6);
-
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
+ visualTest(
+ 'glyph resource allocation does not corrupt textures',
+ async (p5, screenshot) => {
+ p5.createCanvas(100, 100, p5.WEBGL);
+ vi.spyOn(p5._renderer, 'maxCachedGlyphs').mockReturnValue(6);
+
+ const font = await p5.loadFont('test/unit/assets/Inconsolata-Bold.ttf');
+
+ p5.textFont(font);
+ p5.clear();
+ p5.textSize(10);
+ p5.textAlign(p5.LEFT, p5.TOP);
+ for (let i = 0; i < 100; i++) {
+ const x = -p5.width / 2 + (i % 10) * 10;
+ const y = -p5.height / 2 + p5.floor(i / 10) * 10;
+ p5.text(String.fromCharCode(33 + i), x, y);
+ }
- p5.textFont(font);
- p5.clear();
- p5.textSize(10);
- p5.textAlign(p5.LEFT, p5.TOP);
- for (let i = 0; i < 100; i++) {
- const x = -p5.width/2 + (i % 10) * 10;
- const y = -p5.height/2 + p5.floor(i / 10) * 10;
- p5.text(String.fromCharCode(33 + i), x, y);
+ screenshot();
}
+ );
- screenshot();
- });
-
- visualTest('text renders correctly after geometry with many indices', async (p5, screenshot) => {
- p5.createCanvas(100, 100, p5.WEBGL);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
+ visualTest(
+ 'text renders correctly after geometry with many indices',
+ async (p5, screenshot) => {
+ p5.createCanvas(100, 100, p5.WEBGL);
+ const font = await p5.loadFont('test/unit/assets/Inconsolata-Bold.ttf');
- p5.background(255);
- p5.noStroke();
+ p5.background(255);
+ p5.noStroke();
- p5.textFont(font);
- p5.textSize(20);
- p5.textAlign(p5.CENTER, p5.CENTER);
- p5.text('Test 1', 0, -20);
+ p5.textFont(font);
+ p5.textSize(20);
+ p5.textAlign(p5.CENTER, p5.CENTER);
+ p5.text('Test 1', 0, -20);
- // Draw a sphere which has many more indices than text
- p5.fill(200, 200, 255);
- p5.sphere(30);
+ // Draw a sphere which has many more indices than text
+ p5.fill(200, 200, 255);
+ p5.sphere(30);
- p5.clearDepth();
+ p5.clearDepth();
- // Draw text - should bind its own index buffer
- p5.fill(0);
- p5.text('Test 2', 0, 20);
+ // Draw text - should bind its own index buffer
+ p5.fill(0);
+ p5.text('Test 2', 0, 20);
- screenshot();
- });
+ screenshot();
+ }
+ );
});
visualSuite('texture()', () => {
@@ -939,13 +1012,16 @@ visualSuite('WebGL', function() {
visualTest('uniformTexture() works', async (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
const tex = await p5.loadImage('test/unit/assets/cat.jpg');
- const shader = p5.baseMaterialShader().modify(() => {
- const texUniform = p5.uniformTexture(() => tex)
- p5.getPixelInputs((inputs) => {
- inputs.color = p5.getTexture(texUniform, inputs.texCoord);
- return inputs;
- });
- }, { p5, tex });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ const texUniform = p5.uniformTexture(() => tex);
+ p5.getPixelInputs(inputs => {
+ inputs.color = p5.getTexture(texUniform, inputs.texCoord);
+ return inputs;
+ });
+ },
+ { p5, tex }
+ );
p5.shader(shader);
p5.rect(-20, -20, 40, 40);
screenshot();
@@ -969,14 +1045,18 @@ visualSuite('WebGL', function() {
positionData.pixels[i * 4 + 3] = 1;
}
positionData.updatePixels();
- const sh = p5.baseMaterialShader().modify(() => {
- const data = p5.uniformTexture(() => positionData);
- p5.getWorldInputs((inputs) => {
- const angle = p5.getTexture(data, [p5.instanceIndex/3, 0]).r * p5.TWO_PI;
- inputs.position.xy += [p5.cos(angle) * 10, p5.sin(angle) * 10];
- return inputs;
- });
- }, { p5, positionData });
+ const sh = p5.baseMaterialShader().modify(
+ () => {
+ const data = p5.uniformTexture(() => positionData);
+ p5.getWorldInputs(inputs => {
+ const angle =
+ p5.getTexture(data, [p5.instanceIndex / 3, 0]).r * p5.TWO_PI;
+ inputs.position.xy += [p5.cos(angle) * 10, p5.sin(angle) * 10];
+ return inputs;
+ });
+ },
+ { p5, positionData }
+ );
const instance = p5.buildGeometry(() => p5.sphere(3));
p5.noStroke();
@@ -987,7 +1067,7 @@ visualSuite('WebGL', function() {
});
});
- visualSuite("Image Based Lighting", function () {
+ visualSuite('Image Based Lighting', function () {
const shinesses = [50, 150];
for (const shininess of shinesses) {
visualTest(
@@ -1029,21 +1109,26 @@ visualSuite('WebGL', function() {
visualSuite('instanced randering', async () => {
visualTest('can draw in a grid with floor()', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs((inputs) => {
- const id = p5.instanceIndex;
- const gridSize = 5;
- const row = p5.floor(id / gridSize);
- const col = id - row * gridSize;
- const blockInnerSize = 10;
- const x = (col - gridSize / 2.0) * blockInnerSize + blockInnerSize/2;
- const y = (gridSize / 2.0 - row) * blockInnerSize - blockInnerSize/2;
- inputs.position += [x, y, 0];
- return inputs;
- });
- }, { p5 });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ const id = p5.instanceIndex;
+ const gridSize = 5;
+ const row = p5.floor(id / gridSize);
+ const col = id - row * gridSize;
+ const blockInnerSize = 10;
+ const x =
+ (col - gridSize / 2.0) * blockInnerSize + blockInnerSize / 2;
+ const y =
+ (gridSize / 2.0 - row) * blockInnerSize - blockInnerSize / 2;
+ inputs.position += [x, y, 0];
+ return inputs;
+ });
+ },
+ { p5 }
+ );
p5.shader(shader);
- const obj = p5.buildGeometry(() => p5.circle(0, 0, 6))
+ const obj = p5.buildGeometry(() => p5.circle(0, 0, 6));
p5.noStroke();
p5.fill(0);
p5.shader(shader);
@@ -1053,113 +1138,142 @@ visualSuite('WebGL', function() {
visualTest('can draw in a grid with int()', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs((inputs) => {
- const id = p5.instanceIndex;
- const gridSize = 5;
- const row = p5.int(id / gridSize);
- const col = id - row * gridSize;
- const blockInnerSize = 10;
- const x = (col - gridSize / 2.0) * blockInnerSize + blockInnerSize/2;
- const y = (gridSize / 2.0 - row) * blockInnerSize - blockInnerSize/2;
- inputs.position += [x, y, 0];
- return inputs;
- });
- }, { p5 });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ const id = p5.instanceIndex;
+ const gridSize = 5;
+ const row = p5.int(id / gridSize);
+ const col = id - row * gridSize;
+ const blockInnerSize = 10;
+ const x =
+ (col - gridSize / 2.0) * blockInnerSize + blockInnerSize / 2;
+ const y =
+ (gridSize / 2.0 - row) * blockInnerSize - blockInnerSize / 2;
+ inputs.position += [x, y, 0];
+ return inputs;
+ });
+ },
+ { p5 }
+ );
p5.shader(shader);
- const obj = p5.buildGeometry(() => p5.circle(0, 0, 6))
+ const obj = p5.buildGeometry(() => p5.circle(0, 0, 6));
p5.noStroke();
p5.fill(0);
p5.shader(shader);
p5.model(obj, 25);
screenshot();
});
- visualTest('instanceID in fragment hook colors instances', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const numInstances = 4;
- const shader = p5.baseMaterialShader().modify(() => {
- // Vertex hook: position instances in a horizontal row
- p5.getWorldInputs((inputs) => {
- const id = p5.instanceIndex;
- const spacing = 12;
- const offset = (id - (numInstances - 1) / 2.0) * spacing;
- inputs.position.x += offset;
- return inputs;
- });
- // Fragment hook: color each instance based on instanceID
- p5.getFinalColor((color) => {
- const id = p5.instanceIndex;
- const t = id / (numInstances - 1.0);
- color = [t, t, t, 1];
- return color;
- });
- }, { p5, numInstances });
- p5.background(128);
- p5.noStroke();
- p5.shader(shader);
- const obj = p5.buildGeometry(() => p5.circle(0, 0, 10));
- p5.model(obj, numInstances);
- screenshot();
- });
+ visualTest(
+ 'instanceID in fragment hook colors instances',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const numInstances = 4;
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ // Vertex hook: position instances in a horizontal row
+ p5.getWorldInputs(inputs => {
+ const id = p5.instanceIndex;
+ const spacing = 12;
+ const offset = (id - (numInstances - 1) / 2.0) * spacing;
+ inputs.position.x += offset;
+ return inputs;
+ });
+ // Fragment hook: color each instance based on instanceID
+ p5.getFinalColor(color => {
+ const id = p5.instanceIndex;
+ const t = id / (numInstances - 1.0);
+ color = [t, t, t, 1];
+ return color;
+ });
+ },
+ { p5, numInstances }
+ );
+ p5.background(128);
+ p5.noStroke();
+ p5.shader(shader);
+ const obj = p5.buildGeometry(() => p5.circle(0, 0, 10));
+ p5.model(obj, numInstances);
+ screenshot();
+ }
+ );
- visualTest('instances() API draws multiple spaced primitives', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const count = 5;
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs((inputs) => {
- let spacing = p5.width / count;
- inputs.position.x += (p5.instanceIndex - (count - 1) / 2.0) * spacing;
- return inputs;
- });
- }, { p5, count });
- p5.background(220);
- p5.lights();
- p5.noStroke();
- p5.fill('red');
- p5.shader(shader);
- p5.instances(count).sphere(7);
- screenshot();
- });
+ visualTest(
+ 'instances() API draws multiple spaced primitives',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const count = 5;
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ let spacing = p5.width / count;
+ inputs.position.x +=
+ (p5.instanceIndex - (count - 1) / 2.0) * spacing;
+ return inputs;
+ });
+ },
+ { p5, count }
+ );
+ p5.background(220);
+ p5.lights();
+ p5.noStroke();
+ p5.fill('red');
+ p5.shader(shader);
+ p5.instances(count).sphere(7);
+ screenshot();
+ }
+ );
- visualTest('instances() API draws multiple spaced 2D rects', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const count = 3;
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs(inputs => {
- let spacing = p5.width / count;
- inputs.position.x += (p5.instanceIndex - (count - 1) / 2.0) * spacing;
- return inputs;
- });
- }, { p5, count });
- p5.background(220);
- p5.noStroke();
- p5.fill('blue');
- p5.shader(shader);
- p5.instances(count).rect(-5, -5, 10, 10);
- screenshot();
- });
+ visualTest(
+ 'instances() API draws multiple spaced 2D rects',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const count = 3;
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ let spacing = p5.width / count;
+ inputs.position.x +=
+ (p5.instanceIndex - (count - 1) / 2.0) * spacing;
+ return inputs;
+ });
+ },
+ { p5, count }
+ );
+ p5.background(220);
+ p5.noStroke();
+ p5.fill('blue');
+ p5.shader(shader);
+ p5.instances(count).rect(-5, -5, 10, 10);
+ screenshot();
+ }
+ );
- visualTest('instances() API draws instanced lines and points', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const count = 3;
- const spaceFn = () => {
- p5.getWorldInputs(inputs => {
- let spacing = p5.width / count;
- inputs.position.x += (p5.instanceIndex - (count - 1) / 2.0) * spacing;
- return inputs;
- });
- };
- const matShader = p5.buildMaterialShader(spaceFn, { p5, count });
- const strShader = p5.buildStrokeShader(spaceFn, { p5, count });
- p5.background(220);
- p5.stroke(0);
- p5.strokeWeight(3);
- p5.shader(matShader);
- p5.strokeShader(strShader);
- p5.instances(count).line(0, -15, 0, 0, 15, 0);
- p5.instances(count).point(0, 0, 0);
- screenshot();
- });
+ visualTest(
+ 'instances() API draws instanced lines and points',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const count = 3;
+ const spaceFn = () => {
+ p5.getWorldInputs(inputs => {
+ let spacing = p5.width / count;
+ inputs.position.x +=
+ (p5.instanceIndex - (count - 1) / 2.0) * spacing;
+ return inputs;
+ });
+ };
+ const matShader = p5.buildMaterialShader(spaceFn, { p5, count });
+ const strShader = p5.buildStrokeShader(spaceFn, { p5, count });
+ p5.background(220);
+ p5.stroke(0);
+ p5.strokeWeight(3);
+ p5.shader(matShader);
+ p5.strokeShader(strShader);
+ p5.instances(count).line(0, -15, 0, 0, 15, 0);
+ p5.instances(count).point(0, 0, 0);
+ screenshot();
+ }
+ );
visualTest('instances() API draws instanced curves', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
@@ -1200,14 +1314,17 @@ visualSuite('WebGL', function() {
visualTest('random() colors a basic shader', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseColorShader().modify(() => {
- p5.randomSeed(12);
- p5.getFinalColor((color) => {
- const value = p5.random(0.2, 0.9);
- color = [value, value, value, 1];
- return color;
- });
- }, { p5 });
+ const shader = p5.baseColorShader().modify(
+ () => {
+ p5.randomSeed(12);
+ p5.getFinalColor(color => {
+ const value = p5.random(0.2, 0.9);
+ color = [value, value, value, 1];
+ return color;
+ });
+ },
+ { p5 }
+ );
p5.background(0);
p5.noStroke();
p5.shader(shader);
@@ -1216,52 +1333,64 @@ visualSuite('WebGL', function() {
});
visualTest('randomGaussian() colors a basic shader', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseColorShader().modify(() => {
- p5.randomSeed(12);
- p5.getFinalColor((color) => {
- const value = p5.randomGaussian(0.5, 0.1);
- color = [value, value, value, 1];
- return color;
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const shader = p5.baseColorShader().modify(
+ () => {
+ p5.randomSeed(12);
+ p5.getFinalColor(color => {
+ const value = p5.randomGaussian(0.5, 0.1);
+ color = [value, value, value, 1];
+ return color;
+ });
+ },
+ { p5 }
+ );
+ p5.background(0);
+ p5.noStroke();
+ p5.shader(shader);
+ p5.plane(50, 50);
+ screenshot();
});
- }, { p5 });
- p5.background(0);
- p5.noStroke();
- p5.shader(shader);
- p5.plane(50, 50);
- screenshot();
-});
-visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.randomSeed(7);
- p5.getPixelInputs(inputs => {
- let sum = p5.float(0.0);
- for (let i = 0; i < 20; i++) {
- sum = sum + p5.randomGaussian(0.5, 0.2);
+ visualTest(
+ 'randomGaussian() in a fragment loop averages to the mean',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.randomSeed(7);
+ p5.getPixelInputs(inputs => {
+ let sum = p5.float(0.0);
+ for (let i = 0; i < 20; i++) {
+ sum = sum + p5.randomGaussian(0.5, 0.2);
+ }
+ const avg = sum / 20;
+ inputs.color = [avg, avg, avg, 1.0];
+ return inputs;
+ });
+ },
+ { p5 }
+ );
+ p5.background(0);
+ p5.noStroke();
+ p5.shader(shader);
+ p5.plane(50, 50);
+ screenshot();
}
- const avg = sum / 20;
- inputs.color = [avg, avg, avg, 1.0];
- return inputs;
- });
- }, { p5 });
- p5.background(0);
- p5.noStroke();
- p5.shader(shader);
- p5.plane(50, 50);
- screenshot();
-});
+ );
visualTest('uses width/height in getFinalColor', (p5, screenshot) => {
p5.createCanvas(60, 60, p5.WEBGL);
p5.pixelDensity(1);
- const firstShader = p5.baseColorShader().modify(() => {
- p5.getFinalColor((color) => {
- color = [p5.width / 60, p5.height / 60, 0, 1];
- return color;
- });
- }, { p5 });
+ const firstShader = p5.baseColorShader().modify(
+ () => {
+ p5.getFinalColor(color => {
+ color = [p5.width / 60, p5.height / 60, 0, 1];
+ return color;
+ });
+ },
+ { p5 }
+ );
p5.background(0);
p5.shader(firstShader);
p5.noStroke();
@@ -1272,16 +1401,15 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
visualTest('lerp maps to mix in strands context', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
// lerp should behave identically to mix inside strands
- const shader = p5.baseColorShader().modify(() => {
- p5.getFinalColor((color) => {
- color = p5.lerp(
- [1, 0, 0, 1],
- [0, 0, 1, 1],
- 0.5
- );
- return color;
- });
- }, { p5 });
+ const shader = p5.baseColorShader().modify(
+ () => {
+ p5.getFinalColor(color => {
+ color = p5.lerp([1, 0, 0, 1], [0, 0, 1, 1], 0.5);
+ return color;
+ });
+ },
+ { p5 }
+ );
p5.background(0);
p5.shader(shader);
p5.noStroke();
@@ -1289,33 +1417,38 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
screenshot();
});
- visualTest('mix produces same result as lerp in strands', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- // mix directly, should produce identical output to lerp test above
- const shader = p5.baseColorShader().modify(() => {
- p5.getFinalColor((color) => {
- color = p5.mix(
- [1, 0, 0, 1],
- [0, 0, 1, 1],
- 0.5
- );
- return color;
- });
- }, { p5 });
- p5.background(0);
- p5.shader(shader);
- p5.noStroke();
- p5.plane(50, 50);
- screenshot();
- });
+ visualTest(
+ 'mix produces same result as lerp in strands',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ // mix directly, should produce identical output to lerp test above
+ const shader = p5.baseColorShader().modify(
+ () => {
+ p5.getFinalColor(color => {
+ color = p5.mix([1, 0, 0, 1], [0, 0, 1, 1], 0.5);
+ return color;
+ });
+ },
+ { p5 }
+ );
+ p5.background(0);
+ p5.shader(shader);
+ p5.noStroke();
+ p5.plane(50, 50);
+ screenshot();
+ }
+ );
visualTest('texCoord is available in getFinalColor', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseColorShader().modify(() => {
- p5.finalColor.begin();
- p5.finalColor.set([p5.finalColor.texCoord, 0, 1]);
- p5.finalColor.end();
- }, { p5 });
+ const shader = p5.baseColorShader().modify(
+ () => {
+ p5.finalColor.begin();
+ p5.finalColor.set([p5.finalColor.texCoord, 0, 1]);
+ p5.finalColor.end();
+ },
+ { p5 }
+ );
p5.background(0);
p5.shader(shader);
p5.noStroke();
@@ -1324,29 +1457,38 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
});
visualSuite('auto-return for shader hooks', () => {
- visualTest('auto-returns input struct when return is omitted', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs((inputs) => {
- inputs.position.x += 10;
- // No explicit return - should auto-return inputs
- });
- }, { p5 });
- p5.background(255);
- p5.noStroke();
- p5.shader(shader);
- p5.sphere(20);
- screenshot();
- });
+ visualTest(
+ 'auto-returns input struct when return is omitted',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ inputs.position.x += 10;
+ // No explicit return - should auto-return inputs
+ });
+ },
+ { p5 }
+ );
+ p5.background(255);
+ p5.noStroke();
+ p5.shader(shader);
+ p5.sphere(20);
+ screenshot();
+ }
+ );
visualTest('explicit return still works', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs((inputs) => {
- inputs.position.x += 10;
- return inputs; // Explicit return should still work
- });
- }, { p5 });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ inputs.position.x += 10;
+ return inputs; // Explicit return should still work
+ });
+ },
+ { p5 }
+ );
p5.background(255);
p5.noStroke();
p5.shader(shader);
@@ -1356,12 +1498,15 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
visualTest('auto-return works with getObjectInputs', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getObjectInputs((inputs) => {
- inputs.position.x += 0.25;
- // No explicit return
- });
- }, { p5 });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getObjectInputs(inputs => {
+ inputs.position.x += 0.25;
+ // No explicit return
+ });
+ },
+ { p5 }
+ );
p5.background(255);
p5.lights();
p5.fill('red');
@@ -1375,12 +1520,15 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
visualTest('auto-return works with getCameraInputs', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getCameraInputs((inputs) => {
- inputs.position.x += 10;
- // No explicit return
- });
- }, { p5 });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getCameraInputs(inputs => {
+ inputs.position.x += 10;
+ // No explicit return
+ });
+ },
+ { p5 }
+ );
p5.background(255);
p5.lights();
p5.fill('red');
@@ -1392,34 +1540,43 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
screenshot();
});
- visualTest('auto-return preserves multiple property modifications', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs((inputs) => {
- inputs.position.x += 5;
- inputs.position.y += 5;
- inputs.normal.x += 0.5;
- inputs.normal = p5.normalize(inputs.normal);
- // No explicit return - all modifications should be preserved
- });
- }, { p5 });
- p5.background(255);
- p5.lights();
- p5.fill('red');
- p5.noStroke();
- p5.shader(shader);
- p5.sphere(20);
- screenshot();
- });
+ visualTest(
+ 'auto-return preserves multiple property modifications',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ inputs.position.x += 5;
+ inputs.position.y += 5;
+ inputs.normal.x += 0.5;
+ inputs.normal = p5.normalize(inputs.normal);
+ // No explicit return - all modifications should be preserved
+ });
+ },
+ { p5 }
+ );
+ p5.background(255);
+ p5.lights();
+ p5.fill('red');
+ p5.noStroke();
+ p5.shader(shader);
+ p5.sphere(20);
+ screenshot();
+ }
+ );
visualTest('auto-return works with getPixelInputs', (p5, screenshot) => {
p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getPixelInputs((inputs) => {
- inputs.color = p5.vec4(1.0, 0.0, 0.0, 1.0); // Red
- // No explicit return
- });
- }, { p5 });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getPixelInputs(inputs => {
+ inputs.color = p5.vec4(1.0, 0.0, 0.0, 1.0); // Red
+ // No explicit return
+ });
+ },
+ { p5 }
+ );
p5.background(255);
p5.noStroke();
p5.shader(shader);
@@ -1428,7 +1585,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
});
});
- visualTest('Strands tutorial', function(p5, screenshot) {
+ visualTest('Strands tutorial', function (p5, screenshot) {
// From Luke Plowden's Intro to Strands tutorial
// https://beta.p5js.org/tutorials/intro-to-p5-strands/
@@ -1442,7 +1599,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
function semiSphere() {
let id = p5.instanceIndex;
- let theta = rand2([id, 0.1234]) * p5.TWO_PI + time / 100000;
+ let theta = rand2([id, 0.1234]) * p5.TWO_PI + time / 100000;
let phi = rand2([id, 3.321]) * p5.PI + time / 50000;
let r = skyRadius;
@@ -1453,12 +1610,12 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
return [x, y, z];
}
- p5.getWorldInputs((inputs) => {
+ p5.getWorldInputs(inputs => {
inputs.position += semiSphere();
return inputs;
});
- p5.getObjectInputs((inputs) => {
+ p5.getObjectInputs(inputs => {
let size = 1 + 0.5 * p5.sin(time * 0.002 + p5.instanceIndex);
inputs.position *= size;
return inputs;
@@ -1476,7 +1633,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
coord = p5.floor(coord * pixelSize) / pixelSize;
let col = p5.getTexture(canvasContent, coord);
- return col//[coord, 0, 1];
+ return col; //[coord, 0, 1];
});
}
@@ -1495,7 +1652,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
}
p5.createCanvas(200, 200, p5.WEBGL);
- const stars = p5.buildGeometry(() => p5.sphere(4, 4, 2))
+ const stars = p5.buildGeometry(() => p5.sphere(4, 4, 2));
const originalImage = p5.createFramebuffer();
function fresnelShaderCallback({ p5 }) {
@@ -1503,104 +1660,132 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
const fresnelBias = p5.uniformFloat(-0.1);
const fresnelScale = p5.uniformFloat(2);
- p5.getCameraInputs((inputs) => {
+ p5.getCameraInputs(inputs => {
let n = p5.normalize(inputs.normal);
let v = p5.normalize(-inputs.position);
let base = 1.0 - p5.dot(n, v);
let fresnel = fresnelScale * p5.pow(base, fresnelPower) + fresnelBias;
- let col = p5.mix([0, 0, 0], [1, .5, .7], fresnel);
+ let col = p5.mix([0, 0, 0], [1, 0.5, 0.7], fresnel);
inputs.color = [col, 1];
return inputs;
});
}
- const starShader = p5.baseMaterialShader().modify(starShaderCallback, { p5 });
- const starStrokeShader = p5.baseStrokeShader().modify(starShaderCallback, { p5 })
- const fresnelShader = p5.baseColorShader().modify(fresnelShaderCallback, { p5 });
- const bloomShader = p5.baseFilterShader().modify(bloomShaderCallback, { p5, originalImage });
- const pixelateShader = p5.baseFilterShader().modify(pixelateShaderCallback, { p5 });
+ const starShader = p5
+ .baseMaterialShader()
+ .modify(starShaderCallback, { p5 });
+ const starStrokeShader = p5
+ .baseStrokeShader()
+ .modify(starShaderCallback, { p5 });
+ const fresnelShader = p5
+ .baseColorShader()
+ .modify(fresnelShaderCallback, { p5 });
+ const bloomShader = p5
+ .baseFilterShader()
+ .modify(bloomShaderCallback, { p5, originalImage });
+ const pixelateShader = p5
+ .baseFilterShader()
+ .modify(pixelateShaderCallback, { p5 });
originalImage.begin();
p5.background(0);
- p5.push()
- p5.strokeWeight(2)
- p5.stroke(255,0,0)
- p5.fill(255,100, 150)
- p5.strokeShader(starStrokeShader)
+ p5.push();
+ p5.strokeWeight(2);
+ p5.stroke(255, 0, 0);
+ p5.fill(255, 100, 150);
+ p5.strokeShader(starStrokeShader);
p5.shader(starShader);
p5.model(stars, 100);
- p5.pop()
+ p5.pop();
- p5.push()
- p5.shader(fresnelShader)
- p5.noStroke()
+ p5.push();
+ p5.shader(fresnelShader);
+ p5.noStroke();
p5.sphere(30);
p5.filter(pixelateShader);
- p5.pop()
+ p5.pop();
originalImage.end();
- p5.imageMode(p5.CENTER)
- p5.image(originalImage, 0, 0)
+ p5.imageMode(p5.CENTER);
+ p5.image(originalImage, 0, 0);
- p5.filter(p5.BLUR, 5)
+ p5.filter(p5.BLUR, 5);
p5.filter(bloomShader);
screenshot();
});
- visualTest('setUniform with p5.Vector offsets position', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const myShader = p5.baseMaterialShader().modify(() => {
- const uOffset = p5.uniformVec2('uOffset');
- p5.worldInputs.begin();
- p5.worldInputs.position.xy += uOffset;
- p5.worldInputs.end();
- }, { p5 });
- p5.background(200);
- p5.shader(myShader);
- myShader.setUniform('uOffset', p5.createVector(10, -10));
- p5.noStroke();
- p5.fill('red');
- p5.circle(0, 0, 20);
- screenshot();
- });
+ visualTest(
+ 'setUniform with p5.Vector offsets position',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const myShader = p5.baseMaterialShader().modify(
+ () => {
+ const uOffset = p5.uniformVec2('uOffset');
+ p5.worldInputs.begin();
+ p5.worldInputs.position.xy += uOffset;
+ p5.worldInputs.end();
+ },
+ { p5 }
+ );
+ p5.background(200);
+ p5.shader(myShader);
+ myShader.setUniform('uOffset', p5.createVector(10, -10));
+ p5.noStroke();
+ p5.fill('red');
+ p5.circle(0, 0, 20);
+ screenshot();
+ }
+ );
- visualTest('setUniform with p5.Color sets final color', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const myShader = p5.baseMaterialShader().modify(() => {
- const uColor = p5.uniformVec4('uColor');
- p5.finalColor.begin();
- p5.finalColor.set(uColor);
- p5.finalColor.end();
- }, { p5 });
- p5.background(200);
- p5.shader(myShader);
- myShader.setUniform('uColor', p5.color(0, 100, 200));
- p5.noStroke();
- p5.circle(0, 0, 30);
- screenshot();
- });
+ visualTest(
+ 'setUniform with p5.Color sets final color',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const myShader = p5.baseMaterialShader().modify(
+ () => {
+ const uColor = p5.uniformVec4('uColor');
+ p5.finalColor.begin();
+ p5.finalColor.set(uColor);
+ p5.finalColor.end();
+ },
+ { p5 }
+ );
+ p5.background(200);
+ p5.shader(myShader);
+ myShader.setUniform('uColor', p5.color(0, 100, 200));
+ p5.noStroke();
+ p5.circle(0, 0, 30);
+ screenshot();
+ }
+ );
- visualTest('hook returning a fresh struct (not the struct argument) applies modifications', (p5, screenshot) => {
- p5.createCanvas(50, 50, p5.WEBGL);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.worldInputs.begin();
- p5.worldInputs.set({
- position: p5.worldInputs.position.add([10, 0, 0]),
- normal: p5.worldInputs.normal,
- texCoord: p5.worldInputs.texCoord,
- color: [1, 0, 0, 1],
- });
- p5.worldInputs.end();
- }, { p5 });
- p5.background(0);
- p5.noStroke();
- p5.shader(shader);
- p5.plane(20, 20);
- screenshot();
- });
+ visualTest(
+ 'hook returning a fresh struct (not the struct argument) applies modifications',
+ (p5, screenshot) => {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.worldInputs.begin();
+ p5.worldInputs.set({
+ position: p5.worldInputs.position.add([10, 0, 0]),
+ normal: p5.worldInputs.normal,
+ texCoord: p5.worldInputs.texCoord,
+ color: [1, 0, 0, 1]
+ });
+ p5.worldInputs.end();
+ },
+ { p5 }
+ );
+ p5.background(0);
+ p5.noStroke();
+ p5.shader(shader);
+ p5.plane(20, 20);
+ screenshot();
+ }
+ );
});
visualSuite('setUniform', () => {
@@ -1638,45 +1823,51 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
});
});
- visualSuite('Transforms', function() {
- visualTest('translate() moves shapes in x and y axes', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(200);
- p5.noStroke();
+ visualSuite('Transforms', function () {
+ visualTest(
+ 'translate() moves shapes in x and y axes',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(200);
+ p5.noStroke();
- // Red circle at origin
- p5.fill('red');
- p5.circle(0, 0, 10);
+ // Red circle at origin
+ p5.fill('red');
+ p5.circle(0, 0, 10);
- // Green circle translated by (15, 0)
- p5.push();
- p5.translate(15, 0);
- p5.fill('green');
- p5.circle(0, 0, 10);
- p5.pop();
+ // Green circle translated by (15, 0)
+ p5.push();
+ p5.translate(15, 0);
+ p5.fill('green');
+ p5.circle(0, 0, 10);
+ p5.pop();
- // Blue circle translated by (0, 15)
- p5.push();
- p5.translate(0, 15);
- p5.fill('blue');
- p5.circle(0, 0, 10);
- p5.pop();
+ // Blue circle translated by (0, 15)
+ p5.push();
+ p5.translate(0, 15);
+ p5.fill('blue');
+ p5.circle(0, 0, 10);
+ p5.pop();
- screenshot();
- });
+ screenshot();
+ }
+ );
- visualTest('rotate() rotates shapes around z-axis', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(200);
- p5.noStroke();
- p5.fill('red');
- p5.rectMode(p5.CENTER);
- p5.rotate(p5.PI / 4);
- p5.rect(0, 0, 30, 30);
- screenshot();
- });
+ visualTest(
+ 'rotate() rotates shapes around z-axis',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(200);
+ p5.noStroke();
+ p5.fill('red');
+ p5.rectMode(p5.CENTER);
+ p5.rotate(p5.PI / 4);
+ p5.rect(0, 0, 30, 30);
+ screenshot();
+ }
+ );
- visualTest('scale() uniformly scales shapes', function(p5, screenshot) {
+ visualTest('scale() uniformly scales shapes', function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
p5.background(200);
p5.noStroke();
@@ -1696,8 +1887,8 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
});
});
- visualSuite('media assets', function() {
- visualTest('drawing gifs', async function(p5, screenshot) {
+ visualSuite('media assets', function () {
+ visualTest('drawing gifs', async function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
const gif = await p5.loadImage('/test/unit/assets/nyan_cat.gif');
p5.imageMode(p5.CENTER);
@@ -1705,21 +1896,24 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
screenshot();
});
- visualTest('drawing gifs after a time delay', async function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- const gif = await p5.loadImage('/test/unit/assets/nyan_cat.gif');
- p5.imageMode(p5.CENTER);
- p5.image(gif, 0, 0);
- p5.clear()
- // Simulate waiting for successive draw calls
- p5._lastRealFrameTime += 300;
- p5.image(gif, 0, 0);
- screenshot();
- });
+ visualTest(
+ 'drawing gifs after a time delay',
+ async function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ const gif = await p5.loadImage('/test/unit/assets/nyan_cat.gif');
+ p5.imageMode(p5.CENTER);
+ p5.image(gif, 0, 0);
+ p5.clear();
+ // Simulate waiting for successive draw calls
+ p5._lastRealFrameTime += 300;
+ p5.image(gif, 0, 0);
+ screenshot();
+ }
+ );
});
- visualSuite('2D Shapes', function() {
- visualTest('rect() rounded into a circle', function(p5, screenshot) {
+ visualSuite('2D Shapes', function () {
+ visualTest('rect() rounded into a circle', function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
p5.background(255);
p5.noStroke();
@@ -1728,7 +1922,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
screenshot();
});
- visualTest('TRIANGLE_FAN with per-vertex fills', function(p5, screenshot) {
+ visualTest('TRIANGLE_FAN with per-vertex fills', function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
p5.background(255);
p5.beginShape(p5.TRIANGLE_FAN);
@@ -1738,50 +1932,41 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
const r = 20;
p5.fill('blue');
for (let i = 0; i <= n; i++) {
- const angle = i/n * p5.TWO_PI;
- p5.vertex(r*p5.cos(angle), r*p5.sin(angle));
+ const angle = (i / n) * p5.TWO_PI;
+ p5.vertex(r * p5.cos(angle), r * p5.sin(angle));
}
p5.endShape();
screenshot();
});
- visualTest('TRIANGLE_FAN in p5.Geometry with per-vertex fills', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(255);
- const geom = p5.buildGeometry(() => {
- p5.beginShape(p5.TRIANGLE_FAN);
- p5.fill('red');
- p5.vertex(0, 0);
- const n = 10;
- const r = 20;
- p5.fill('blue');
- for (let i = 0; i <= n; i++) {
- const angle = i/n * p5.TWO_PI;
- p5.vertex(r*p5.cos(angle), r*p5.sin(angle));
- }
- p5.endShape();
- });
- p5.model(geom);
- screenshot();
- });
-
- visualTest('TRIANGLE_STRIP with per-vertex fills', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(255);
- p5.beginShape(p5.TRIANGLE_STRIP);
- const n = 6;
- for (let i = 0; i < n; i++) {
- p5.fill(i % 2 === 0 ? 'red' : 'blue');
- p5.vertex(p5.map(i, 0, n - 1, -20, 20), i % 2 === 0 ? -10 : 10);
+ visualTest(
+ 'TRIANGLE_FAN in p5.Geometry with per-vertex fills',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(255);
+ const geom = p5.buildGeometry(() => {
+ p5.beginShape(p5.TRIANGLE_FAN);
+ p5.fill('red');
+ p5.vertex(0, 0);
+ const n = 10;
+ const r = 20;
+ p5.fill('blue');
+ for (let i = 0; i <= n; i++) {
+ const angle = (i / n) * p5.TWO_PI;
+ p5.vertex(r * p5.cos(angle), r * p5.sin(angle));
+ }
+ p5.endShape();
+ });
+ p5.model(geom);
+ screenshot();
}
- p5.endShape();
- screenshot();
- });
+ );
- visualTest('TRIANGLE_STRIP in p5.Geometry with per-vertex fills', function(p5, screenshot) {
- p5.createCanvas(50, 50, p5.WEBGL);
- p5.background(255);
- const geom = p5.buildGeometry(() => {
+ visualTest(
+ 'TRIANGLE_STRIP with per-vertex fills',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(255);
p5.beginShape(p5.TRIANGLE_STRIP);
const n = 6;
for (let i = 0; i < n; i++) {
@@ -1789,14 +1974,32 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
p5.vertex(p5.map(i, 0, n - 1, -20, 20), i % 2 === 0 ? -10 : 10);
}
p5.endShape();
- });
- p5.model(geom);
- screenshot();
- });
+ screenshot();
+ }
+ );
+
+ visualTest(
+ 'TRIANGLE_STRIP in p5.Geometry with per-vertex fills',
+ function (p5, screenshot) {
+ p5.createCanvas(50, 50, p5.WEBGL);
+ p5.background(255);
+ const geom = p5.buildGeometry(() => {
+ p5.beginShape(p5.TRIANGLE_STRIP);
+ const n = 6;
+ for (let i = 0; i < n; i++) {
+ p5.fill(i % 2 === 0 ? 'red' : 'blue');
+ p5.vertex(p5.map(i, 0, n - 1, -20, 20), i % 2 === 0 ? -10 : 10);
+ }
+ p5.endShape();
+ });
+ p5.model(geom);
+ screenshot();
+ }
+ );
});
- visualSuite('3D Primitives', function() {
- visualTest('cylinder() renders correctly', function(p5, screenshot) {
+ visualSuite('3D Primitives', function () {
+ visualTest('cylinder() renders correctly', function (p5, screenshot) {
p5.createCanvas(100, 100, p5.WEBGL);
p5.background(255);
@@ -1813,7 +2016,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
screenshot();
});
- visualTest('cone() renders correctly', function(p5, screenshot) {
+ visualTest('cone() renders correctly', function (p5, screenshot) {
p5.createCanvas(100, 100, p5.WEBGL);
p5.background(255);
@@ -1830,24 +2033,27 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
screenshot();
});
- visualTest('ellipsoid() renders with non-uniform radii', function(p5, screenshot) {
- p5.createCanvas(100, 100, p5.WEBGL);
- p5.background(255);
+ visualTest(
+ 'ellipsoid() renders with non-uniform radii',
+ function (p5, screenshot) {
+ p5.createCanvas(100, 100, p5.WEBGL);
+ p5.background(255);
- p5.ambientLight(100);
- p5.directionalLight(255, 255, 255, 0, 0, -1);
+ p5.ambientLight(100);
+ p5.directionalLight(255, 255, 255, 0, 0, -1);
- p5.noStroke();
- p5.fill(200);
+ p5.noStroke();
+ p5.fill(200);
- p5.rotateX(p5.PI / 6);
- p5.rotateY(p5.PI / 4);
+ p5.rotateX(p5.PI / 6);
+ p5.rotateY(p5.PI / 4);
- p5.ellipsoid(20, 30, 40);
- screenshot();
- });
+ p5.ellipsoid(20, 30, 40);
+ screenshot();
+ }
+ );
- visualTest('torus() renders correctly', function(p5, screenshot) {
+ visualTest('torus() renders correctly', function (p5, screenshot) {
p5.createCanvas(100, 100, p5.WEBGL);
p5.background(255);
@@ -1865,82 +2071,85 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre
});
});
- visualSuite('Tessellation', function() {
- visualTest('Handles nearly identical consecutive vertices', function(p5, screenshot) {
- p5.createCanvas(400, 400, p5.WEBGL);
-
- const contours = [
- [
- [-3.8642425537109375, -6.120738636363637, 0],
- [3.2025188099254267, -6.120738636363637, 0],
- [3.2025188099254267, -4.345170454545455, 0],
- [-3.8642425537109375, -4.345170454545455, 0],
- [-3.8642425537109375, -6.120738636363637, 0]
- ],
- [
- [-1.8045834628018462, 4.177556818181818, 0],
- [-1.8045834628018462, -9.387784090909093, 0],
- [0.29058699174360836, -9.387784090909093, 0],
- [0.2905869917436083, 3.609374411367136, 0],
- [0.31044303036623855, 4.068235883781435, 0],
- [0.38522861430307975, 4.522728865422799, 0],
- [0.548044378107245, 4.941051136363637, 0],
- [0.8364672032828204, 5.2932224887960775, 0],
- [1.2227602871981542, 5.526988636363637, 0],
- [1.6572258237923885, 5.634502949876295, 0],
- [2.101666537198154, 5.669034090909091, 0],
- [2.6695604948237173, 5.633568761673102, 0],
- [3.0249619917436084, 5.5625, 0],
- [3.4510983553799726, 7.4446022727272725, 0],
- [2.8568950819856695, 7.613138889205699, 0],
- [2.3751340936529037, 7.676962586830456, 0],
- [1.8892600236717598, 7.693181792704519, 0],
- [1.2922705720786674, 7.649533731133848, 0],
- [0.7080836288276859, 7.519788939617751, 0],
- [0.14854153719815422, 7.311434659090909, 0],
- [-0.38643934048179873, 7.00959666478984, 0],
- [-0.858113258144025, 6.61653855366859, 0],
- [-1.25415732643821, 6.1484375, 0],
- [-1.5108595282965422, 5.697682732328092, 0],
- [-1.6824918355513252, 5.207533878495854, 0],
- [-1.7762971052870198, 4.695933154267308, 0],
- [-1.8045834628018462, 4.177556818181818, 0]
- ]
- ];
-
- p5.background('red');
- p5.push();
- p5.stroke(0);
- p5.fill('#EEE');
- p5.scale(15);
- p5.beginShape();
- for (const contour of contours) {
- p5.beginContour();
- for (const v of contour) {
- p5.vertex(...v);
+ visualSuite('Tessellation', function () {
+ visualTest(
+ 'Handles nearly identical consecutive vertices',
+ function (p5, screenshot) {
+ p5.createCanvas(400, 400, p5.WEBGL);
+
+ const contours = [
+ [
+ [-3.8642425537109375, -6.120738636363637, 0],
+ [3.2025188099254267, -6.120738636363637, 0],
+ [3.2025188099254267, -4.345170454545455, 0],
+ [-3.8642425537109375, -4.345170454545455, 0],
+ [-3.8642425537109375, -6.120738636363637, 0]
+ ],
+ [
+ [-1.8045834628018462, 4.177556818181818, 0],
+ [-1.8045834628018462, -9.387784090909093, 0],
+ [0.29058699174360836, -9.387784090909093, 0],
+ [0.2905869917436083, 3.609374411367136, 0],
+ [0.31044303036623855, 4.068235883781435, 0],
+ [0.38522861430307975, 4.522728865422799, 0],
+ [0.548044378107245, 4.941051136363637, 0],
+ [0.8364672032828204, 5.2932224887960775, 0],
+ [1.2227602871981542, 5.526988636363637, 0],
+ [1.6572258237923885, 5.634502949876295, 0],
+ [2.101666537198154, 5.669034090909091, 0],
+ [2.6695604948237173, 5.633568761673102, 0],
+ [3.0249619917436084, 5.5625, 0],
+ [3.4510983553799726, 7.4446022727272725, 0],
+ [2.8568950819856695, 7.613138889205699, 0],
+ [2.3751340936529037, 7.676962586830456, 0],
+ [1.8892600236717598, 7.693181792704519, 0],
+ [1.2922705720786674, 7.649533731133848, 0],
+ [0.7080836288276859, 7.519788939617751, 0],
+ [0.14854153719815422, 7.311434659090909, 0],
+ [-0.38643934048179873, 7.00959666478984, 0],
+ [-0.858113258144025, 6.61653855366859, 0],
+ [-1.25415732643821, 6.1484375, 0],
+ [-1.5108595282965422, 5.697682732328092, 0],
+ [-1.6824918355513252, 5.207533878495854, 0],
+ [-1.7762971052870198, 4.695933154267308, 0],
+ [-1.8045834628018462, 4.177556818181818, 0]
+ ]
+ ];
+
+ p5.background('red');
+ p5.push();
+ p5.stroke(0);
+ p5.fill('#EEE');
+ p5.scale(15);
+ p5.beginShape();
+ for (const contour of contours) {
+ p5.beginContour();
+ for (const v of contour) {
+ p5.vertex(...v);
+ }
+ p5.endContour();
}
- p5.endContour();
- }
- p5.endShape();
+ p5.endShape();
- p5.stroke(0, 255, 0);
- p5.strokeWeight(5);
- p5.beginShape(p5.POINTS);
- for (const contour of contours) {
- for (const v of contour) {
- p5.vertex(...v);
+ p5.stroke(0, 255, 0);
+ p5.strokeWeight(5);
+ p5.beginShape(p5.POINTS);
+ for (const contour of contours) {
+ for (const v of contour) {
+ p5.vertex(...v);
+ }
}
- }
- p5.endShape();
- p5.pop();
+ p5.endShape();
+ p5.pop();
- screenshot();
- });
+ screenshot();
+ }
+ );
});
visualTest(
'user-set uSampler on custom shader is not overridden',
- function(p5, screenshot) {
+ function (p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
const myShader = p5.createFilterShader(`precision highp float;
diff --git a/test/unit/visual/cases/webgpu.js b/test/unit/visual/cases/webgpu.js
index 255c4adf44..1e341bc9b3 100644
--- a/test/unit/visual/cases/webgpu.js
+++ b/test/unit/visual/cases/webgpu.js
@@ -1,18 +1,18 @@
-import { vi } from "vitest";
-import p5 from "../../../../src/app";
-import { visualSuite, visualTest } from "../visualTest";
-import rendererWebGPU from "../../../../src/webgpu/p5.RendererWebGPU";
+import { vi } from 'vitest';
+import p5 from '../../../../src/app';
+import { visualSuite, visualTest } from '../visualTest';
+import rendererWebGPU from '../../../../src/webgpu/p5.RendererWebGPU';
p5.registerAddon(rendererWebGPU);
-visualSuite("WebGPU", function () {
- visualSuite("Shaders", function () {
+visualSuite('WebGPU', function () {
+ visualSuite('Shaders', function () {
visualTest(
- "The color shader runs successfully",
+ 'The color shader runs successfully',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background("white");
- for (const [i, color] of ["red", "lime", "blue"].entries()) {
+ p5.background('white');
+ for (const [i, color] of ['red', 'lime', 'blue'].entries()) {
p5.push();
p5.rotate(p5.TWO_PI * (i / 3));
p5.fill(color);
@@ -22,15 +22,15 @@ visualSuite("WebGPU", function () {
p5.pop();
}
await screenshot();
- },
+ }
);
visualTest(
- "The stroke shader runs successfully",
+ 'The stroke shader runs successfully',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background("white");
- for (const [i, color] of ["red", "lime", "blue"].entries()) {
+ p5.background('white');
+ for (const [i, color] of ['red', 'lime', 'blue'].entries()) {
p5.push();
p5.rotate(p5.TWO_PI * (i / 3));
p5.translate(15, 0);
@@ -40,20 +40,20 @@ visualSuite("WebGPU", function () {
p5.pop();
}
await screenshot();
- },
+ }
);
visualTest(
- "The material shader runs successfully",
+ 'The material shader runs successfully',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background("white");
+ p5.background('white');
p5.ambientLight(50);
p5.directionalLight(100, 100, 100, 0, 1, -1);
p5.pointLight(155, 155, 155, 0, -200, 500);
p5.specularMaterial(255);
p5.shininess(300);
- for (const [i, color] of ["red", "lime", "blue"].entries()) {
+ for (const [i, color] of ['red', 'lime', 'blue'].entries()) {
p5.push();
p5.rotate(p5.TWO_PI * (i / 3));
p5.fill(color);
@@ -63,37 +63,37 @@ visualSuite("WebGPU", function () {
p5.pop();
}
await screenshot();
- },
+ }
);
- visualTest("Shader hooks can be used", async function (p5, screenshot) {
+ visualTest('Shader hooks can be used', async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
const myFill = p5.baseMaterialShader().modify({
- "Vertex getWorldInputs": `(inputs: Vertex) {
+ 'Vertex getWorldInputs': `(inputs: Vertex) {
var result = inputs;
result.position.y += 10.0 * sin(inputs.position.x * 0.25);
return result;
- }`,
+ }`
});
const myStroke = p5.baseStrokeShader().modify({
- "StrokeVertex getWorldInputs": `(inputs: StrokeVertex) {
+ 'StrokeVertex getWorldInputs': `(inputs: StrokeVertex) {
var result = inputs;
result.position.y += 10.0 * sin(inputs.position.x * 0.25);
return result;
- }`,
+ }`
});
- p5.background("black");
+ p5.background('black');
p5.shader(myFill);
p5.strokeShader(myStroke);
- p5.fill("red");
- p5.stroke("white");
+ p5.fill('red');
+ p5.stroke('white');
p5.strokeWeight(5);
p5.circle(0, 0, 30);
await screenshot();
});
visualTest(
- "Textures in the material shader work",
+ 'Textures in the material shader work',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
const tex = p5.createImage(50, 50);
@@ -112,18 +112,21 @@ visualSuite("WebGPU", function () {
p5.plane(p5.width, p5.height);
await screenshot();
- },
+ }
);
- visualTest('Instanced rendering', async function(p5, screenshot) {
+ visualTest('Instanced rendering', async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
const model = p5.buildGeometry(() => p5.sphere(5));
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs((inputs) => {
- inputs.position += (p5.instanceIndex - 1) * 15
- return inputs;
- });
- }, { p5 });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ inputs.position += (p5.instanceIndex - 1) * 15;
+ return inputs;
+ });
+ },
+ { p5 }
+ );
p5.noStroke();
p5.fill(0);
p5.shader(shader);
@@ -131,7 +134,7 @@ visualSuite("WebGPU", function () {
await screenshot();
});
- visualTest('Strands tutorial', async function(p5, screenshot) {
+ visualTest('Strands tutorial', async function (p5, screenshot) {
// From Luke Plowden's Intro to Strands tutorial
// https://beta.p5js.org/tutorials/intro-to-p5-strands/
@@ -145,7 +148,7 @@ visualSuite("WebGPU", function () {
function semiSphere() {
let id = p5.instanceIndex;
- let theta = rand2([id, 0.1234]) * p5.TWO_PI + time / 100000;
+ let theta = rand2([id, 0.1234]) * p5.TWO_PI + time / 100000;
let phi = rand2([id, 3.321]) * p5.PI + time / 50000;
let r = skyRadius;
@@ -156,12 +159,12 @@ visualSuite("WebGPU", function () {
return [x, y, z];
}
- p5.getWorldInputs((inputs) => {
+ p5.getWorldInputs(inputs => {
inputs.position += semiSphere();
return inputs;
});
- p5.getObjectInputs((inputs) => {
+ p5.getObjectInputs(inputs => {
let size = 1 + 0.5 * p5.sin(time * 0.002 + p5.instanceIndex);
inputs.position *= size;
return inputs;
@@ -179,7 +182,7 @@ visualSuite("WebGPU", function () {
coord = p5.floor(coord * pixelSize) / pixelSize;
let col = p5.getTexture(canvasContent, coord);
- return col//[coord, 0, 1];
+ return col; //[coord, 0, 1];
});
}
@@ -198,7 +201,7 @@ visualSuite("WebGPU", function () {
}
await p5.createCanvas(200, 200, p5.WEBGPU);
- const stars = p5.buildGeometry(() => p5.sphere(4, 4, 2))
+ const stars = p5.buildGeometry(() => p5.sphere(4, 4, 2));
const originalImage = p5.createFramebuffer();
function fresnelShaderCallback({ p5 }) {
@@ -206,310 +209,386 @@ visualSuite("WebGPU", function () {
const fresnelBias = p5.uniformFloat(-0.1);
const fresnelScale = p5.uniformFloat(2);
- p5.getCameraInputs((inputs) => {
+ p5.getCameraInputs(inputs => {
let n = p5.normalize(inputs.normal);
let v = p5.normalize(-inputs.position);
let base = 1.0 - p5.dot(n, v);
let fresnel = fresnelScale * p5.pow(base, fresnelPower) + fresnelBias;
- let col = p5.mix([0, 0, 0], [1, .5, .7], fresnel);
+ let col = p5.mix([0, 0, 0], [1, 0.5, 0.7], fresnel);
inputs.color = [col, 1];
return inputs;
});
}
- const starShader = p5.baseMaterialShader().modify(starShaderCallback, { p5 });
- const starStrokeShader = p5.baseStrokeShader().modify(starShaderCallback, { p5 })
- const fresnelShader = p5.baseColorShader().modify(fresnelShaderCallback, { p5 });
- const bloomShader = p5.baseFilterShader().modify(bloomShaderCallback, { p5, originalImage });
- const pixelateShader = p5.baseFilterShader().modify(pixelateShaderCallback, { p5 });
+ const starShader = p5
+ .baseMaterialShader()
+ .modify(starShaderCallback, { p5 });
+ const starStrokeShader = p5
+ .baseStrokeShader()
+ .modify(starShaderCallback, { p5 });
+ const fresnelShader = p5
+ .baseColorShader()
+ .modify(fresnelShaderCallback, { p5 });
+ const bloomShader = p5
+ .baseFilterShader()
+ .modify(bloomShaderCallback, { p5, originalImage });
+ const pixelateShader = p5
+ .baseFilterShader()
+ .modify(pixelateShaderCallback, { p5 });
originalImage.begin();
p5.background(0);
- p5.push()
- p5.strokeWeight(2)
- p5.stroke(255,0,0)
- p5.fill(255,100, 150)
- p5.strokeShader(starStrokeShader)
+ p5.push();
+ p5.strokeWeight(2);
+ p5.stroke(255, 0, 0);
+ p5.fill(255, 100, 150);
+ p5.strokeShader(starStrokeShader);
p5.shader(starShader);
p5.model(stars, 100);
- p5.pop()
+ p5.pop();
- p5.push()
- p5.shader(fresnelShader)
- p5.noStroke()
+ p5.push();
+ p5.shader(fresnelShader);
+ p5.noStroke();
p5.sphere(30);
p5.filter(pixelateShader);
- p5.pop()
+ p5.pop();
originalImage.end();
- p5.imageMode(p5.CENTER)
- p5.image(originalImage, 0, 0)
+ p5.imageMode(p5.CENTER);
+ p5.image(originalImage, 0, 0);
- p5.filter(p5.BLUR, 5)
+ p5.filter(p5.BLUR, 5);
p5.filter(bloomShader);
await screenshot();
});
- visualTest('filter shaders with flat API', async function(p5, screenshot) {
+ visualTest('filter shaders with flat API', async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
p5.background(255);
p5.noStroke();
p5.fill(0);
p5.circle(0, 0, 20);
- const invert = p5.buildFilterShader(({ p5 }) => {
- p5.filterColor.begin();
- const regular = p5.getTexture(
- p5.filterColor.canvasContent,
- p5.filterColor.texCoord
- );
- const inverted = [1 - regular.rgb, regular.a];
- p5.filterColor.set(inverted);
- p5.filterColor.end();
- }, { p5 });
- p5.filter(invert);
- await screenshot();
- });
-
- visualTest('filter shaders can sample a texture inside a conditional branch', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background(255);
- p5.noStroke();
- p5.fill(0);
- p5.circle(0, 0, 20);
- // This shader only samples the texture for pixels in the left half of the
- // canvas, exercising getTexture() inside a non-uniform conditional
- const conditionalInvert = p5.buildFilterShader(({ p5 }) => {
- p5.filterColor.begin();
- if (p5.filterColor.texCoord.x < 0.5) {
- const col = p5.getTexture(
+ const invert = p5.buildFilterShader(
+ ({ p5 }) => {
+ p5.filterColor.begin();
+ const regular = p5.getTexture(
p5.filterColor.canvasContent,
p5.filterColor.texCoord
);
- p5.filterColor.set([1 - col.rgb, col.a]);
- } else {
- p5.filterColor.set([0, 0, 1, 1]);
- }
- p5.filterColor.end();
- }, { p5 });
- p5.filter(conditionalInvert);
+ const inverted = [1 - regular.rgb, regular.a];
+ p5.filterColor.set(inverted);
+ p5.filterColor.end();
+ },
+ { p5 }
+ );
+ p5.filter(invert);
await screenshot();
});
- visualTest('instanceID in fragment hook colors instances (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const numInstances = 4;
- const shader = p5.baseMaterialShader().modify(() => {
- // Vertex hook: position instances in a horizontal row
- p5.getWorldInputs((inputs) => {
- const id = p5.instanceIndex;
- const spacing = 12;
- const offset = (id - (numInstances - 1) / 2.0) * spacing;
- inputs.position.x += offset;
- return inputs;
- });
- // Fragment hook: color each instance based on instanceID
- p5.getFinalColor((color) => {
- const id = p5.instanceIndex;
- const t = id / (numInstances - 1.0);
- color = [t, t, t, 1];
- return color;
- });
- }, { p5, numInstances });
- p5.background(128);
- p5.noStroke();
- p5.shader(shader);
- const obj = p5.buildGeometry(() => p5.circle(0, 0, 10));
- p5.model(obj, numInstances);
- await screenshot();
- });
+ visualTest(
+ 'filter shaders can sample a texture inside a conditional branch',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ p5.background(255);
+ p5.noStroke();
+ p5.fill(0);
+ p5.circle(0, 0, 20);
+ // This shader only samples the texture for pixels in the left half of the
+ // canvas, exercising getTexture() inside a non-uniform conditional
+ const conditionalInvert = p5.buildFilterShader(
+ ({ p5 }) => {
+ p5.filterColor.begin();
+ if (p5.filterColor.texCoord.x < 0.5) {
+ const col = p5.getTexture(
+ p5.filterColor.canvasContent,
+ p5.filterColor.texCoord
+ );
+ p5.filterColor.set([1 - col.rgb, col.a]);
+ } else {
+ p5.filterColor.set([0, 0, 1, 1]);
+ }
+ p5.filterColor.end();
+ },
+ { p5 }
+ );
+ p5.filter(conditionalInvert);
+ await screenshot();
+ }
+ );
- visualTest('instances() API draws multiple spaced primitives (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const count = 5;
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs((inputs) => {
- let spacing = p5.width / count;
- inputs.position.x += (p5.instanceIndex - (count - 1) / 2.0) * spacing;
- return inputs;
- });
- }, { p5, count });
- p5.background(220);
- p5.lights();
- p5.noStroke();
- p5.fill('red');
- p5.shader(shader);
- p5.instances(count).sphere(7);
- await screenshot();
- });
+ visualTest(
+ 'instanceID in fragment hook colors instances (WebGPU)',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const numInstances = 4;
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ // Vertex hook: position instances in a horizontal row
+ p5.getWorldInputs(inputs => {
+ const id = p5.instanceIndex;
+ const spacing = 12;
+ const offset = (id - (numInstances - 1) / 2.0) * spacing;
+ inputs.position.x += offset;
+ return inputs;
+ });
+ // Fragment hook: color each instance based on instanceID
+ p5.getFinalColor(color => {
+ const id = p5.instanceIndex;
+ const t = id / (numInstances - 1.0);
+ color = [t, t, t, 1];
+ return color;
+ });
+ },
+ { p5, numInstances }
+ );
+ p5.background(128);
+ p5.noStroke();
+ p5.shader(shader);
+ const obj = p5.buildGeometry(() => p5.circle(0, 0, 10));
+ p5.model(obj, numInstances);
+ await screenshot();
+ }
+ );
- visualTest('instances() API draws multiple spaced 2D rects (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const count = 3;
- const shader = p5.baseMaterialShader().modify(() => {
- p5.getWorldInputs(inputs => {
- let spacing = p5.width / count;
- inputs.position.x += (p5.instanceIndex - (count - 1) / 2.0) * spacing;
- return inputs;
- });
- }, { p5, count });
- p5.background(220);
- p5.noStroke();
- p5.fill('blue');
- p5.shader(shader);
- p5.instances(count).rect(-5, -5, 10, 10);
- await screenshot();
- });
+ visualTest(
+ 'instances() API draws multiple spaced primitives (WebGPU)',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const count = 5;
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ let spacing = p5.width / count;
+ inputs.position.x +=
+ (p5.instanceIndex - (count - 1) / 2.0) * spacing;
+ return inputs;
+ });
+ },
+ { p5, count }
+ );
+ p5.background(220);
+ p5.lights();
+ p5.noStroke();
+ p5.fill('red');
+ p5.shader(shader);
+ p5.instances(count).sphere(7);
+ await screenshot();
+ }
+ );
- visualTest('instances() API draws instanced lines and points (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const count = 3;
- const spaceFn = () => {
- p5.getWorldInputs(inputs => {
- let spacing = p5.width / count;
- inputs.position.x += (p5.instanceIndex - (count - 1) / 2.0) * spacing;
- return inputs;
- });
- };
- const matShader = p5.buildMaterialShader(spaceFn, { p5, count });
- const strShader = p5.buildStrokeShader(spaceFn, { p5, count });
- p5.background(220);
- p5.stroke(0);
- p5.strokeWeight(3);
- p5.shader(matShader);
- p5.strokeShader(strShader);
- p5.instances(count).line(0, -15, 0, 0, 15, 0);
- p5.instances(count).point(0, 0, 0);
- await screenshot();
- });
+ visualTest(
+ 'instances() API draws multiple spaced 2D rects (WebGPU)',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const count = 3;
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.getWorldInputs(inputs => {
+ let spacing = p5.width / count;
+ inputs.position.x +=
+ (p5.instanceIndex - (count - 1) / 2.0) * spacing;
+ return inputs;
+ });
+ },
+ { p5, count }
+ );
+ p5.background(220);
+ p5.noStroke();
+ p5.fill('blue');
+ p5.shader(shader);
+ p5.instances(count).rect(-5, -5, 10, 10);
+ await screenshot();
+ }
+ );
- visualTest('instances() API draws instanced curves (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const count = 3;
- const spaceFn = () => {
- p5.getWorldInputs(inputs => {
- let spacing = p5.width / count;
- inputs.position.x += (p5.instanceIndex - (count - 1) / 2.0) * spacing;
- return inputs;
- });
- };
- const matShader = p5.buildMaterialShader(spaceFn, { p5, count });
- const strShader = p5.buildStrokeShader(spaceFn, { p5, count });
- p5.background(220);
- p5.stroke(0);
- p5.strokeWeight(2);
- p5.noFill();
- p5.shader(matShader);
- p5.strokeShader(strShader);
- p5.instances(count).bezier(-5, -5, 0, -2, 5, 0, 2, -5, 0, 5, 5, 0);
- p5.instances(count).spline(-5, 5, 0, -2, -5, 0, 2, 5, 0, 5, -5, 0);
- await screenshot();
- });
+ visualTest(
+ 'instances() API draws instanced lines and points (WebGPU)',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const count = 3;
+ const spaceFn = () => {
+ p5.getWorldInputs(inputs => {
+ let spacing = p5.width / count;
+ inputs.position.x +=
+ (p5.instanceIndex - (count - 1) / 2.0) * spacing;
+ return inputs;
+ });
+ };
+ const matShader = p5.buildMaterialShader(spaceFn, { p5, count });
+ const strShader = p5.buildStrokeShader(spaceFn, { p5, count });
+ p5.background(220);
+ p5.stroke(0);
+ p5.strokeWeight(3);
+ p5.shader(matShader);
+ p5.strokeShader(strShader);
+ p5.instances(count).line(0, -15, 0, 0, 15, 0);
+ p5.instances(count).point(0, 0, 0);
+ await screenshot();
+ }
+ );
- visualTest('random() colors a basic shader (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const shader = p5.baseColorShader().modify(() => {
- p5.randomSeed(12);
- p5.getFinalColor((color) => {
- const value = p5.random(0.2, 0.9);
- color = [value, value, value, 1];
- return color;
- });
- }, { p5 });
- p5.background(0);
- p5.noStroke();
- p5.shader(shader);
- p5.plane(50, 50);
- await screenshot();
- });
+ visualTest(
+ 'instances() API draws instanced curves (WebGPU)',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const count = 3;
+ const spaceFn = () => {
+ p5.getWorldInputs(inputs => {
+ let spacing = p5.width / count;
+ inputs.position.x +=
+ (p5.instanceIndex - (count - 1) / 2.0) * spacing;
+ return inputs;
+ });
+ };
+ const matShader = p5.buildMaterialShader(spaceFn, { p5, count });
+ const strShader = p5.buildStrokeShader(spaceFn, { p5, count });
+ p5.background(220);
+ p5.stroke(0);
+ p5.strokeWeight(2);
+ p5.noFill();
+ p5.shader(matShader);
+ p5.strokeShader(strShader);
+ p5.instances(count).bezier(-5, -5, 0, -2, 5, 0, 2, -5, 0, 5, 5, 0);
+ p5.instances(count).spline(-5, 5, 0, -2, -5, 0, 2, 5, 0, 5, -5, 0);
+ await screenshot();
+ }
+ );
- visualTest('random() in a fragment loop averages to gray (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.randomSeed(7);
- p5.getPixelInputs(inputs => {
- let sum = p5.float(0.0);
- for (let i = 0; i < 20; i++) {
- sum = sum + p5.random();
- }
- const avg = sum / 20;
- inputs.color = [avg, avg, avg, 1.0];
- return inputs;
- });
- }, { p5 });
+ visualTest(
+ 'random() colors a basic shader (WebGPU)',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const shader = p5.baseColorShader().modify(
+ () => {
+ p5.randomSeed(12);
+ p5.getFinalColor(color => {
+ const value = p5.random(0.2, 0.9);
+ color = [value, value, value, 1];
+ return color;
+ });
+ },
+ { p5 }
+ );
+ p5.background(0);
+ p5.noStroke();
+ p5.shader(shader);
+ p5.plane(50, 50);
+ await screenshot();
+ }
+ );
+
+ visualTest(
+ 'random() in a fragment loop averages to gray (WebGPU)',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.randomSeed(7);
+ p5.getPixelInputs(inputs => {
+ let sum = p5.float(0.0);
+ for (let i = 0; i < 20; i++) {
+ sum = sum + p5.random();
+ }
+ const avg = sum / 20;
+ inputs.color = [avg, avg, avg, 1.0];
+ return inputs;
+ });
+ },
+ { p5 }
+ );
+ p5.background(0);
+ p5.noStroke();
+ p5.shader(shader);
+ p5.plane(50, 50);
+ await screenshot();
+ }
+ );
+
+ visualTest(
+ 'hook returning a fresh struct (not the struct argument) applies modifications',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.worldInputs.begin();
+ p5.worldInputs.set({
+ position: p5.worldInputs.position.add([10, 0, 0]),
+ normal: p5.worldInputs.normal,
+ texCoord: p5.worldInputs.texCoord,
+ color: [1, 0, 0, 1]
+ });
+ p5.worldInputs.end();
+ },
+ { p5 }
+ );
+ p5.background(0);
+ p5.noStroke();
+ p5.shader(shader);
+ p5.plane(20, 20);
+ await screenshot();
+ }
+ );
+ });
+
+ visualTest(
+ 'randomGaussian() colors a basic shader (WebGPU)',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ const shader = p5.baseColorShader().modify(
+ () => {
+ p5.randomSeed(12);
+ p5.getFinalColor(color => {
+ const value = p5.randomGaussian(0.5, 0.1);
+ color = [value, value, value, 1];
+ return color;
+ });
+ },
+ { p5 }
+ );
p5.background(0);
p5.noStroke();
p5.shader(shader);
p5.plane(50, 50);
await screenshot();
- });
+ }
+ );
- visualTest('hook returning a fresh struct (not the struct argument) applies modifications', async function(p5, screenshot) {
+ visualTest(
+ 'randomGaussian() in a fragment loop averages to the mean (WebGPU)',
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.worldInputs.begin();
- p5.worldInputs.set({
- position: p5.worldInputs.position.add([10, 0, 0]),
- normal: p5.worldInputs.normal,
- texCoord: p5.worldInputs.texCoord,
- color: [1, 0, 0, 1],
- });
- p5.worldInputs.end();
- }, { p5 });
+ const shader = p5.baseMaterialShader().modify(
+ () => {
+ p5.randomSeed(7);
+ p5.getPixelInputs(inputs => {
+ let sum = p5.float(0.0);
+ for (let i = 0; i < 20; i++) {
+ sum = sum + p5.randomGaussian(0.5, 0.2);
+ }
+ const avg = sum / 20;
+ inputs.color = [avg, avg, avg, 1.0];
+ return inputs;
+ });
+ },
+ { p5 }
+ );
p5.background(0);
p5.noStroke();
p5.shader(shader);
- p5.plane(20, 20);
+ p5.plane(50, 50);
await screenshot();
- });
- });
-
- visualTest('randomGaussian() colors a basic shader (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const shader = p5.baseColorShader().modify(() => {
- p5.randomSeed(12);
- p5.getFinalColor((color) => {
- const value = p5.randomGaussian(0.5, 0.1);
- color = [value, value, value, 1];
- return color;
- });
- }, { p5 });
- p5.background(0);
- p5.noStroke();
- p5.shader(shader);
- p5.plane(50, 50);
- await screenshot();
-});
-
-visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- const shader = p5.baseMaterialShader().modify(() => {
- p5.randomSeed(7);
- p5.getPixelInputs(inputs => {
- let sum = p5.float(0.0);
- for (let i = 0; i < 20; i++) {
- sum = sum + p5.randomGaussian(0.5, 0.2);
- }
- const avg = sum / 20;
- inputs.color = [avg, avg, avg, 1.0];
- return inputs;
- });
- }, { p5 });
- p5.background(0);
- p5.noStroke();
- p5.shader(shader);
- p5.plane(50, 50);
- await screenshot();
-});
-
+ }
+ );
- visualSuite('filters', function() {
- const setupSketch = async (p5) => {
+ visualSuite('filters', function () {
+ const setupSketch = async p5 => {
await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.translate(-p5.width/2, -p5.height/2);
+ p5.translate(-p5.width / 2, -p5.height / 2);
p5.clear();
p5.noStroke();
p5.fill('red');
@@ -524,61 +603,70 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.endShape();
};
- visualTest('It can apply GRAY', async function(p5, screenshot) {
+ visualTest('It can apply GRAY', async function (p5, screenshot) {
await setupSketch(p5);
p5.filter(p5.GRAY);
await screenshot();
});
- visualTest('It can apply INVERT', async function(p5, screenshot) {
+ visualTest('It can apply INVERT', async function (p5, screenshot) {
await setupSketch(p5);
p5.filter(p5.INVERT);
await screenshot();
});
- visualTest('It can apply THRESHOLD', async function(p5, screenshot) {
+ visualTest('It can apply THRESHOLD', async function (p5, screenshot) {
await setupSketch(p5);
p5.filter(p5.THRESHOLD);
await screenshot();
});
- visualTest('It can apply THRESHOLD with a value', async function(p5, screenshot) {
- await setupSketch(p5);
- p5.filter(p5.THRESHOLD, 0.8);
- await screenshot();
- });
- visualTest('It can apply POSTERIZE', async function(p5, screenshot) {
+ visualTest(
+ 'It can apply THRESHOLD with a value',
+ async function (p5, screenshot) {
+ await setupSketch(p5);
+ p5.filter(p5.THRESHOLD, 0.8);
+ await screenshot();
+ }
+ );
+ visualTest('It can apply POSTERIZE', async function (p5, screenshot) {
await setupSketch(p5);
p5.filter(p5.THRESHOLD);
await screenshot();
});
- visualTest('It can apply POSTERIZE with a value', async function(p5, screenshot) {
- await setupSketch(p5);
- p5.filter(p5.THRESHOLD, 2);
- await screenshot();
- });
- visualTest('It can apply BLUR', async function(p5, screenshot) {
+ visualTest(
+ 'It can apply POSTERIZE with a value',
+ async function (p5, screenshot) {
+ await setupSketch(p5);
+ p5.filter(p5.THRESHOLD, 2);
+ await screenshot();
+ }
+ );
+ visualTest('It can apply BLUR', async function (p5, screenshot) {
await setupSketch(p5);
p5.filter(p5.BLUR, 5);
await screenshot();
});
- visualTest('It can apply BLUR with a value', async function(p5, screenshot) {
- await setupSketch(p5);
- p5.filter(p5.BLUR, 10);
- await screenshot();
- });
- visualTest('It can apply ERODE (4x)', async function(p5, screenshot) {
+ visualTest(
+ 'It can apply BLUR with a value',
+ async function (p5, screenshot) {
+ await setupSketch(p5);
+ p5.filter(p5.BLUR, 10);
+ await screenshot();
+ }
+ );
+ visualTest('It can apply ERODE (4x)', async function (p5, screenshot) {
await setupSketch(p5);
for (let i = 0; i < 4; i++) p5.filter(p5.ERODE);
await screenshot();
});
- visualTest('It can apply DILATE (4x)', async function(p5, screenshot) {
+ visualTest('It can apply DILATE (4x)', async function (p5, screenshot) {
await setupSketch(p5);
for (let i = 0; i < 4; i++) p5.filter(p5.DILATE);
await screenshot();
});
});
- visualSuite("Canvas Resizing", function () {
+ visualSuite('Canvas Resizing', function () {
visualTest(
- "Main canvas drawing after resize",
+ 'Main canvas drawing after resize',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Resize the canvas
@@ -589,13 +677,13 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.noStroke();
p5.circle(0, 0, 20);
await screenshot();
- },
+ }
);
});
- visualSuite("Framebuffers", function () {
+ visualSuite('Framebuffers', function () {
visualTest(
- "Basic framebuffer draw to canvas",
+ 'Basic framebuffer draw to canvas',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Create a framebuffer
@@ -616,11 +704,11 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.plane(25, 25);
await screenshot();
- },
+ }
);
visualTest(
- "Framebuffer with different sizes",
+ 'Framebuffer with different sizes',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Create two different sized framebuffers
@@ -660,10 +748,10 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.pop();
await screenshot();
- },
+ }
);
- visualTest("Auto-sized framebuffer", async function (p5, screenshot) {
+ visualTest('Auto-sized framebuffer', async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Create auto-sized framebuffer (should match canvas size)
const fbo = p5.createFramebuffer();
@@ -671,7 +759,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
// Draw to the framebuffer
fbo.draw(() => {
p5.background(0);
- p5.translate(-fbo.width / 2, -fbo.height / 2)
+ p5.translate(-fbo.width / 2, -fbo.height / 2);
p5.stroke(255);
p5.strokeWeight(2);
p5.noFill();
@@ -696,7 +784,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
});
visualTest(
- "Auto-sized framebuffer after canvas resize",
+ 'Auto-sized framebuffer after canvas resize',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Create auto-sized framebuffer
@@ -708,7 +796,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
// Draw to the framebuffer after resize
fbo.draw(() => {
p5.background(100, 0, 100);
- p5.translate(-fbo.width / 2, -fbo.height / 2)
+ p5.translate(-fbo.width / 2, -fbo.height / 2);
p5.fill(0, 255, 255);
p5.noStroke();
// Draw a shape that fills the new size
@@ -723,14 +811,18 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.plane(30, 30);
await screenshot();
- },
+ }
);
visualTest(
- "Framebuffer with depth disabled",
+ 'Framebuffer with depth disabled',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
- const fbo = p5.createFramebuffer({ width: 50, height: 50, depth: false });
+ const fbo = p5.createFramebuffer({
+ width: 50,
+ height: 50,
+ depth: false
+ });
fbo.draw(() => {
p5.background(0, 0, 200);
@@ -745,11 +837,11 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.plane(50, 50);
await screenshot();
- },
+ }
);
visualTest(
- "Fixed-size framebuffer after manual resize",
+ 'Fixed-size framebuffer after manual resize',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Create fixed-size framebuffer
@@ -769,7 +861,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
// Draw new content to the resized framebuffer
fbo.draw(() => {
p5.background(200, 255, 100);
- p5.translate(-fbo.width / 2, -fbo.height / 2)
+ p5.translate(-fbo.width / 2, -fbo.height / 2);
p5.fill(200, 0, 100);
p5.noStroke();
// Draw content that uses the new size
@@ -785,13 +877,13 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.plane(35, 25);
await screenshot();
- },
+ }
);
});
- visualSuite("Rendering attributes", function () {
+ visualSuite('Rendering attributes', function () {
visualTest(
- "noSmooth() does not crash and disables antialiasing",
+ 'noSmooth() does not crash and disables antialiasing',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
await p5.noSmooth();
@@ -800,56 +892,51 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.noStroke();
p5.circle(0, 0, 30);
await screenshot();
- },
+ }
);
});
- visualSuite("Clipping", function () {
- visualTest(
- "Basic clipping with circles",
- async function (p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background("white");
+ visualSuite('Clipping', function () {
+ visualTest('Basic clipping with circles', async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ p5.background('white');
- // Draw some circles that extend beyond the clipping area
- p5.fill("red");
- p5.noStroke();
- p5.circle(-15, -15, 25);
- p5.fill("green");
- p5.circle(15, -15, 25);
- p5.fill("blue");
- p5.circle(-15, 15, 25);
- p5.fill("yellow");
- p5.circle(15, 15, 25);
-
- // Apply clipping to a smaller rectangle in the center
- p5.push();
- p5.clip(() => {
- p5.rect(-12.5, -12.5, 25, 25);
- });
+ // Draw some circles that extend beyond the clipping area
+ p5.fill('red');
+ p5.noStroke();
+ p5.circle(-15, -15, 25);
+ p5.fill('green');
+ p5.circle(15, -15, 25);
+ p5.fill('blue');
+ p5.circle(-15, 15, 25);
+ p5.fill('yellow');
+ p5.circle(15, 15, 25);
+
+ // Apply clipping to a smaller rectangle in the center
+ p5.push();
+ p5.clip(() => {
+ p5.rect(-12.5, -12.5, 25, 25);
+ });
- // Draw more circles that should be clipped to the rectangle
- p5.fill("purple");
- p5.circle(-8, -8, 16);
- p5.fill("orange");
- p5.circle(8, 8, 16);
- p5.fill("cyan");
- p5.circle(0, 0, 12);
+ // Draw more circles that should be clipped to the rectangle
+ p5.fill('purple');
+ p5.circle(-8, -8, 16);
+ p5.fill('orange');
+ p5.circle(8, 8, 16);
+ p5.fill('cyan');
+ p5.circle(0, 0, 12);
- p5.pop();
+ p5.pop();
- await screenshot();
- },
- );
+ await screenshot();
+ });
});
visualSuite('Typography', function () {
visualSuite('textFont', function () {
visualTest('with a font file in WebGPU', async function (p5, screenshot) {
await p5.createCanvas(100, 100, p5.WEBGPU);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
+ const font = await p5.loadFont('test/unit/assets/Inconsolata-Bold.ttf');
p5.textFont(font);
p5.textAlign(p5.LEFT, p5.TOP);
p5.textSize(35);
@@ -859,100 +946,118 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
});
visualSuite('textWeight', function () {
- visualTest('can control variable fonts from files in WebGPU', async function (p5, screenshot) {
- await p5.createCanvas(100, 100, p5.WEBGPU);
- const font = await p5.loadFont(
- 'test/unit/assets/BricolageGrotesque-Variable.ttf'
- );
- for (let weight = 400; weight <= 800; weight += 100) {
- p5.push();
- p5.background(255);
- p5.translate(-p5.width/2, -p5.height/2);
- p5.textFont(font);
- p5.textAlign(p5.LEFT, p5.TOP);
- p5.textSize(35);
- p5.textWeight(weight);
- p5.text('p5*js', 0, 10, p5.width);
- p5.pop();
- await screenshot();
- }
- });
- });
-
- visualSuite('textAlign', function () {
- visualSuite('webgpu mode', () => {
- visualTest('all alignments with single word', async function (p5, screenshot) {
- const alignments = [
- { alignX: p5.LEFT, alignY: p5.TOP },
- { alignX: p5.CENTER, alignY: p5.TOP },
- { alignX: p5.RIGHT, alignY: p5.TOP },
- { alignX: p5.LEFT, alignY: p5.CENTER },
- { alignX: p5.CENTER, alignY: p5.CENTER },
- { alignX: p5.RIGHT, alignY: p5.CENTER },
- { alignX: p5.LEFT, alignY: p5.BOTTOM },
- { alignX: p5.CENTER, alignY: p5.BOTTOM },
- { alignX: p5.RIGHT, alignY: p5.BOTTOM }
- ];
-
- await p5.createCanvas(300, 300, p5.WEBGPU);
- p5.translate(-p5.width/2, -p5.height/2);
- p5.textSize(60);
+ visualTest(
+ 'can control variable fonts from files in WebGPU',
+ async function (p5, screenshot) {
+ await p5.createCanvas(100, 100, p5.WEBGPU);
const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
+ 'test/unit/assets/BricolageGrotesque-Variable.ttf'
);
- p5.textFont(font);
- for (const alignment of alignments) {
- p5.background(255);
- p5.textAlign(alignment.alignX, alignment.alignY);
- const bb = p5.textBounds('Single Line', p5.width / 2, p5.height / 2);
+ for (let weight = 400; weight <= 800; weight += 100) {
p5.push();
- p5.push()
- p5.noFill();
- p5.stroke('red');
- p5.rect(bb.x, bb.y, bb.w, bb.h);
- p5.pop()
- p5.fill(0)
- p5.text('Single Line', p5.width / 2, p5.height / 2);
+ p5.background(255);
+ p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textFont(font);
+ p5.textAlign(p5.LEFT, p5.TOP);
+ p5.textSize(35);
+ p5.textWeight(weight);
+ p5.text('p5*js', 0, 10, p5.width);
p5.pop();
await screenshot();
}
- });
+ }
+ );
+ });
- visualTest('all alignments with single line', async function (p5, screenshot) {
- const alignments = [
- { alignX: p5.LEFT, alignY: p5.TOP },
- { alignX: p5.CENTER, alignY: p5.TOP },
- { alignX: p5.RIGHT, alignY: p5.TOP },
- { alignX: p5.LEFT, alignY: p5.CENTER },
- { alignX: p5.CENTER, alignY: p5.CENTER },
- { alignX: p5.RIGHT, alignY: p5.CENTER },
- { alignX: p5.LEFT, alignY: p5.BOTTOM },
- { alignX: p5.CENTER, alignY: p5.BOTTOM },
- { alignX: p5.RIGHT, alignY: p5.BOTTOM }
- ];
-
- await p5.createCanvas(300, 300, p5.WEBGPU);
- p5.translate(-p5.width/2, -p5.height/2);
- p5.textSize(45);
- const font = await p5.loadFont(
- 'test/unit/assets/Inconsolata-Bold.ttf'
- );
- p5.textFont(font);
- for (const alignment of alignments) {
- p5.background(255);
- p5.textAlign(alignment.alignX, alignment.alignY);
- p5.text('Single Line', p5.width / 2, p5.height / 2);
- const bb = p5.textBounds('Single Line', p5.width / 2, p5.height / 2);
- p5.push();
- p5.noFill();
- p5.stroke('red');
- p5.rect(bb.x, bb.y, bb.w, bb.h);
- p5.pop();
- await screenshot();
+ visualSuite('textAlign', function () {
+ visualSuite('webgpu mode', () => {
+ visualTest(
+ 'all alignments with single word',
+ async function (p5, screenshot) {
+ const alignments = [
+ { alignX: p5.LEFT, alignY: p5.TOP },
+ { alignX: p5.CENTER, alignY: p5.TOP },
+ { alignX: p5.RIGHT, alignY: p5.TOP },
+ { alignX: p5.LEFT, alignY: p5.CENTER },
+ { alignX: p5.CENTER, alignY: p5.CENTER },
+ { alignX: p5.RIGHT, alignY: p5.CENTER },
+ { alignX: p5.LEFT, alignY: p5.BOTTOM },
+ { alignX: p5.CENTER, alignY: p5.BOTTOM },
+ { alignX: p5.RIGHT, alignY: p5.BOTTOM }
+ ];
+
+ await p5.createCanvas(300, 300, p5.WEBGPU);
+ p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textSize(60);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.textFont(font);
+ for (const alignment of alignments) {
+ p5.background(255);
+ p5.textAlign(alignment.alignX, alignment.alignY);
+ const bb = p5.textBounds(
+ 'Single Line',
+ p5.width / 2,
+ p5.height / 2
+ );
+ p5.push();
+ p5.push();
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.pop();
+ p5.fill(0);
+ p5.text('Single Line', p5.width / 2, p5.height / 2);
+ p5.pop();
+ await screenshot();
+ }
}
- });
+ );
- visualTest('all alignments with multi-lines and wrap word',
+ visualTest(
+ 'all alignments with single line',
+ async function (p5, screenshot) {
+ const alignments = [
+ { alignX: p5.LEFT, alignY: p5.TOP },
+ { alignX: p5.CENTER, alignY: p5.TOP },
+ { alignX: p5.RIGHT, alignY: p5.TOP },
+ { alignX: p5.LEFT, alignY: p5.CENTER },
+ { alignX: p5.CENTER, alignY: p5.CENTER },
+ { alignX: p5.RIGHT, alignY: p5.CENTER },
+ { alignX: p5.LEFT, alignY: p5.BOTTOM },
+ { alignX: p5.CENTER, alignY: p5.BOTTOM },
+ { alignX: p5.RIGHT, alignY: p5.BOTTOM }
+ ];
+
+ await p5.createCanvas(300, 300, p5.WEBGPU);
+ p5.translate(-p5.width / 2, -p5.height / 2);
+ p5.textSize(45);
+ const font = await p5.loadFont(
+ 'test/unit/assets/Inconsolata-Bold.ttf'
+ );
+ p5.textFont(font);
+ for (const alignment of alignments) {
+ p5.background(255);
+ p5.textAlign(alignment.alignX, alignment.alignY);
+ p5.text('Single Line', p5.width / 2, p5.height / 2);
+ const bb = p5.textBounds(
+ 'Single Line',
+ p5.width / 2,
+ p5.height / 2
+ );
+ p5.push();
+ p5.noFill();
+ p5.stroke('red');
+ p5.rect(bb.x, bb.y, bb.w, bb.h);
+ p5.pop();
+ await screenshot();
+ }
+ }
+ );
+
+ visualTest(
+ 'all alignments with multi-lines and wrap word',
async function (p5, screenshot) {
const alignments = [
{ alignX: p5.LEFT, alignY: p5.TOP },
@@ -967,7 +1072,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
];
await p5.createCanvas(150, 100, p5.WEBGPU);
- p5.translate(-p5.width/2, -p5.height/2);
+ p5.translate(-p5.width / 2, -p5.height / 2);
p5.textSize(20);
p5.textWrap(p5.WORD);
const font = await p5.loadFont(
@@ -1032,7 +1137,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
];
await p5.createCanvas(150, 100, p5.WEBGPU);
- p5.translate(-p5.width/2, -p5.height/2);
+ p5.translate(-p5.width / 2, -p5.height / 2);
p5.textSize(19);
p5.textWrap(p5.CHAR);
const font = await p5.loadFont(
@@ -1097,7 +1202,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
];
await p5.createCanvas(150, 100, p5.WEBGPU);
- p5.translate(-p5.width/2, -p5.height/2);
+ p5.translate(-p5.width / 2, -p5.height / 2);
p5.textSize(20);
const font = await p5.loadFont(
@@ -1122,7 +1227,13 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.fill(0);
p5.noStroke();
- p5.text('Line 1\nLine 2\nLine 3', xPos, yPos, boxWidth, boxHeight);
+ p5.text(
+ 'Line 1\nLine 2\nLine 3',
+ xPos,
+ yPos,
+ boxWidth,
+ boxHeight
+ );
const bb = p5.textBounds(
'Line 1\nLine 2\nLine 3',
xPos,
@@ -1143,9 +1254,9 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
});
});
- visualSuite("Buffer Alignment", function () {
+ visualSuite('Buffer Alignment', function () {
visualTest(
- "buildGeometry with non-4-byte-aligned index buffer size",
+ 'buildGeometry with non-4-byte-aligned index buffer size',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
@@ -1164,9 +1275,12 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
for (let i = 0; i < 1323; i++) {
p5.fill(p5.random(360), 80, 90);
p5.triangle(
- p5.random(-25, 25), p5.random(-25, 25),
- p5.random(-25, 25), p5.random(-25, 25),
- p5.random(-25, 25), p5.random(-25, 25)
+ p5.random(-25, 25),
+ p5.random(-25, 25),
+ p5.random(-25, 25),
+ p5.random(-25, 25),
+ p5.random(-25, 25),
+ p5.random(-25, 25)
);
}
});
@@ -1179,9 +1293,9 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
);
});
- visualSuite("Immediate Mode Buffer Reuse", function () {
+ visualSuite('Immediate Mode Buffer Reuse', function () {
visualTest(
- "beginShape/endShape reuses buffers across frames",
+ 'beginShape/endShape reuses buffers across frames',
async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
@@ -1190,9 +1304,9 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
// isn't FORCED to allocate new buffers, and should be trying
// to reuse them.
const positions = [
- { x: -15, y: -10 }, // Frame 1: left
- { x: 0, y: -10 }, // Frame 2: center
- { x: 15, y: -10 } // Frame 3: right
+ { x: -15, y: -10 }, // Frame 1: left
+ { x: 0, y: -10 }, // Frame 2: center
+ { x: 15, y: -10 } // Frame 3: right
];
for (let frame = 0; frame < 3; frame++) {
@@ -1206,9 +1320,9 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
// same geometry every frame. We expect to see different results
// if it's correctly updating the buffers.
p5.beginShape();
- p5.vertex(pos.x - 5, pos.y + 10); // bottom-left
- p5.vertex(pos.x + 5, pos.y + 10); // bottom-right
- p5.vertex(pos.x, pos.y); // top
+ p5.vertex(pos.x - 5, pos.y + 10); // bottom-left
+ p5.vertex(pos.x + 5, pos.y + 10); // bottom-right
+ p5.vertex(pos.x, pos.y); // top
p5.endShape(p5.CLOSE);
await screenshot();
@@ -1217,7 +1331,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
);
});
- visualSuite("Image Based Lighting", function () {
+ visualSuite('Image Based Lighting', function () {
const shinesses = [50, 150];
for (const shininess of shinesses) {
visualTest(
@@ -1256,7 +1370,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
}
});
- visualSuite('transformation', function() {
+ visualSuite('transformation', function () {
visualTest('outside of push() and pop()', async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
p5.background(200);
@@ -1266,24 +1380,27 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
});
});
- visualSuite('Compute shaders', function() {
+ visualSuite('Compute shaders', function () {
visualTest(
'Storage buffer (float array) can be read in a vertex shader for instanced rendering',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Positions for 3 spheres: (-15,0), (0,0), (15,0)
const positions = p5.createStorage([-15, 0, 0, 0, 15, 0]);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const posData = p5.uniformStorage();
- p5.getWorldInputs((inputs) => {
- const idx = p5.instanceIndex;
- inputs.position.x += posData[idx * 2];
- inputs.position.y += posData[idx * 2 + 1];
- return inputs;
- });
- }, { p5 });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const posData = p5.uniformStorage();
+ p5.getWorldInputs(inputs => {
+ const idx = p5.instanceIndex;
+ inputs.position.x += posData[idx * 2];
+ inputs.position.y += posData[idx * 2 + 1];
+ return inputs;
+ });
+ },
+ { p5 }
+ );
sphereShader.setUniform('posData', positions);
const geo = p5.buildGeometry(() => p5.sphere(5));
@@ -1299,28 +1416,34 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader writes float values to storage buffer, vertex shader reads them',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Start with zeros; compute shader will write [20, -10]
const offset = p5.createStorage(2);
- const computeShader = p5.buildComputeShader(() => {
- const buf = p5.uniformStorage();
- buf[0] = 20;
- buf[1] = -10;
- }, { p5 });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const buf = p5.uniformStorage();
+ buf[0] = 20;
+ buf[1] = -10;
+ },
+ { p5 }
+ );
computeShader.setUniform('buf', offset);
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage();
- p5.getWorldInputs((inputs) => {
- inputs.position.x += buf[0];
- inputs.position.y += buf[1];
- return inputs;
- });
- }, { p5 });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage();
+ p5.getWorldInputs(inputs => {
+ inputs.position.x += buf[0];
+ inputs.position.y += buf[1];
+ return inputs;
+ });
+ },
+ { p5 }
+ );
sphereShader.setUniform('buf', offset);
const geo = p5.buildGeometry(() => p5.sphere(5));
@@ -1336,27 +1459,33 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader reads and transforms float array values',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Initialize with [10, 0] - compute will double x to get [20, 0]
const buf = p5.createStorage([10, 0]);
- const computeShader = p5.buildComputeShader(() => {
- const data = p5.uniformStorage();
- data[0] = data[0] * 2;
- }, { p5 });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const data = p5.uniformStorage();
+ data[0] = data[0] * 2;
+ },
+ { p5 }
+ );
computeShader.setUniform('data', buf);
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const data = p5.uniformStorage();
- p5.getWorldInputs((inputs) => {
- inputs.position.x += data[0];
- inputs.position.y += data[1];
- return inputs;
- });
- }, { p5 });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const data = p5.uniformStorage();
+ p5.getWorldInputs(inputs => {
+ inputs.position.x += data[0];
+ inputs.position.y += data[1];
+ return inputs;
+ });
+ },
+ { p5 }
+ );
sphereShader.setUniform('data', buf);
const geo = p5.buildGeometry(() => p5.sphere(5));
@@ -1370,28 +1499,30 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
}
);
-
visualTest(
'Struct storage buffer fields can be read in a vertex shader for instanced rendering',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Three particles at known positions: left, center, right
const particles = p5.createStorage([
{ position: [-15, 0] },
- { position: [0, 0] },
- { position: [15, 0] },
+ { position: [0, 0] },
+ { position: [15, 0] }
]);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const p = buf[p5.instanceIndex].position;
- inputs.position.x += p.x;
- inputs.position.y += p.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const p = buf[p5.instanceIndex].position;
+ inputs.position.x += p.x;
+ inputs.position.y += p.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1406,25 +1537,28 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Struct storage buffer fields can use p5.Vector values',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Three particles at known positions: left, center, right
const particles = p5.createStorage([
{ position: p5.createVector(-15, 0) },
- { position: p5.createVector(0, 0) },
- { position: p5.createVector(15, 0) },
+ { position: p5.createVector(0, 0) },
+ { position: p5.createVector(15, 0) }
]);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const p = buf[p5.instanceIndex].position;
- inputs.position.x += p.x;
- inputs.position.y += p.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const p = buf[p5.instanceIndex].position;
+ inputs.position.x += p.x;
+ inputs.position.y += p.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1439,25 +1573,28 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Struct storage buffer fields can be read using an inline schema template',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Same layout as above but schema is declared inline rather than via the buffer
const particles = p5.createStorage([
{ position: [-15, 0] },
- { position: [0, 0] },
- { position: [15, 0] },
+ { position: [0, 0] },
+ { position: [15, 0] }
]);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', { position: [0, 0] });
- p5.getWorldInputs((inputs) => {
- const p = buf[p5.instanceIndex].position;
- inputs.position.x += p.x;
- inputs.position.y += p.y;
- return inputs;
- });
- }, { p5 });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', { position: [0, 0] });
+ p5.getWorldInputs(inputs => {
+ const p = buf[p5.instanceIndex].position;
+ inputs.position.x += p.x;
+ inputs.position.y += p.y;
+ return inputs;
+ });
+ },
+ { p5 }
+ );
sphereShader.setUniform('buf', particles);
const geo = p5.buildGeometry(() => p5.sphere(5));
@@ -1473,28 +1610,32 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader writes to struct storage fields, vertex shader reads them',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
- const particles = p5.createStorage([
- { position: [0, 0] },
- ]);
+ const particles = p5.createStorage([{ position: [0, 0] }]);
- const computeShader = p5.buildComputeShader(() => {
- const buf = p5.uniformStorage('buf', particles);
- buf[p5.index.x].position = [15, -10];
- }, { p5, particles });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ buf[p5.index.x].position = [15, -10];
+ },
+ { p5, particles }
+ );
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const pos = buf[0].position;
- inputs.position.x += pos.x;
- inputs.position.y += pos.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const pos = buf[0].position;
+ inputs.position.x += pos.x;
+ inputs.position.y += pos.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1509,29 +1650,35 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader reads and updates struct fields (position += velocity)',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
const particles = p5.createStorage([
- { position: [0, 0], velocity: [15, -10] },
+ { position: [0, 0], velocity: [15, -10] }
]);
- const computeShader = p5.buildComputeShader(() => {
- const buf = p5.uniformStorage('buf', particles);
- const idx = p5.index.x;
- buf[idx].position = buf[idx].position + buf[idx].velocity;
- }, { p5, particles });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ const idx = p5.index.x;
+ buf[idx].position = buf[idx].position + buf[idx].velocity;
+ },
+ { p5, particles }
+ );
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const pos = buf[0].position;
- inputs.position.x += pos.x;
- inputs.position.y += pos.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const pos = buf[0].position;
+ inputs.position.x += pos.x;
+ inputs.position.y += pos.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1546,31 +1693,37 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader updates struct fields via intermediate struct variable',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
const particles = p5.createStorage([
- { position: [0, 0], velocity: [15, -10] },
+ { position: [0, 0], velocity: [15, -10] }
]);
// Store the struct element proxy in a variable and assign through it
- const computeShader = p5.buildComputeShader(() => {
- const buf = p5.uniformStorage('buf', particles);
- const idx = p5.index.x;
- const entry = buf[idx];
- entry.position = entry.position + entry.velocity;
- }, { p5, particles });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ const idx = p5.index.x;
+ const entry = buf[idx];
+ entry.position = entry.position + entry.velocity;
+ },
+ { p5, particles }
+ );
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const pos = buf[0].position;
- inputs.position.x += pos.x;
- inputs.position.y += pos.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const pos = buf[0].position;
+ inputs.position.x += pos.x;
+ inputs.position.y += pos.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1585,32 +1738,38 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader updates struct fields via intermediate field variable',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
const particles = p5.createStorage([
- { position: [0, 0], velocity: [15, -10] },
+ { position: [0, 0], velocity: [15, -10] }
]);
// Store a field value in an intermediate variable, update it, write it back
- const computeShader = p5.buildComputeShader(() => {
- const buf = p5.uniformStorage('buf', particles);
- const idx = p5.index.x;
- let pos = buf[idx].position;
- pos = pos + buf[idx].velocity;
- buf[idx].position = pos;
- }, { p5, particles });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ const idx = p5.index.x;
+ let pos = buf[idx].position;
+ pos = pos + buf[idx].velocity;
+ buf[idx].position = pos;
+ },
+ { p5, particles }
+ );
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const pos = buf[0].position;
- inputs.position.x += pos.x;
- inputs.position.y += pos.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const pos = buf[0].position;
+ inputs.position.x += pos.x;
+ inputs.position.y += pos.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1625,32 +1784,38 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader writes a whole struct element as an object literal',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
const particles = p5.createStorage([
- { position: [0, 0], velocity: [15, -10] },
+ { position: [0, 0], velocity: [15, -10] }
]);
- const computeShader = p5.buildComputeShader(() => {
- const buf = p5.uniformStorage('buf', particles);
- const idx = p5.index.x;
- let pos = buf[idx].position;
- let vel = buf[idx].velocity;
- pos = pos + vel;
- buf[idx] = { position: pos, velocity: vel };
- }, { p5, particles });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ const idx = p5.index.x;
+ let pos = buf[idx].position;
+ let vel = buf[idx].velocity;
+ pos = pos + vel;
+ buf[idx] = { position: pos, velocity: vel };
+ },
+ { p5, particles }
+ );
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const pos = buf[0].position;
- inputs.position.x += pos.x;
- inputs.position.y += pos.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const pos = buf[0].position;
+ inputs.position.x += pos.x;
+ inputs.position.y += pos.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1665,30 +1830,34 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader assigns to a swizzle of a struct vector field',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
- const particles = p5.createStorage([
- { position: [15, 10] },
- ]);
+ const particles = p5.createStorage([{ position: [15, 10] }]);
// Negate position.y via swizzle assignment
- const computeShader = p5.buildComputeShader(() => {
- const buf = p5.uniformStorage('buf', particles);
- const idx = p5.index.x;
- buf[idx].position.y *= -1;
- }, { p5, particles });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ const idx = p5.index.x;
+ buf[idx].position.y *= -1;
+ },
+ { p5, particles }
+ );
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const pos = buf[0].position;
- inputs.position.x += pos.x;
- inputs.position.y += pos.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const pos = buf[0].position;
+ inputs.position.x += pos.x;
+ inputs.position.y += pos.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1703,36 +1872,42 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
visualTest(
'Compute shader assigns to a swizzle of a struct vector field inside an if statement',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
const particles = p5.createStorage([
- { position: [0, 0], velocity: [5, 5] },
+ { position: [0, 0], velocity: [5, 5] }
]);
// Move by velocity, then negate velocity.y if position.y > 0.
// After 1st run: position=[5,5], velocity=[5,-5].
// After 2nd run: position=[10,0], velocity=[5,-5].
- const computeShader = p5.buildComputeShader(() => {
- const buf = p5.uniformStorage('buf', particles);
- const idx = p5.index.x;
- buf[idx].position += buf[idx].velocity;
- if (buf[idx].position.y > 0) {
- buf[idx].velocity.y *= -1;
- }
- }, { p5, particles });
+ const computeShader = p5.buildComputeShader(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ const idx = p5.index.x;
+ buf[idx].position += buf[idx].velocity;
+ if (buf[idx].position.y > 0) {
+ buf[idx].velocity.y *= -1;
+ }
+ },
+ { p5, particles }
+ );
p5.compute(computeShader, 1);
p5.compute(computeShader, 1);
- const sphereShader = p5.baseMaterialShader().modify(() => {
- const buf = p5.uniformStorage('buf', particles);
- p5.getWorldInputs((inputs) => {
- const pos = buf[0].position;
- inputs.position.x += pos.x;
- inputs.position.y += pos.y;
- return inputs;
- });
- }, { p5, particles });
+ const sphereShader = p5.baseMaterialShader().modify(
+ () => {
+ const buf = p5.uniformStorage('buf', particles);
+ p5.getWorldInputs(inputs => {
+ const pos = buf[0].position;
+ inputs.position.x += pos.x;
+ inputs.position.y += pos.y;
+ return inputs;
+ });
+ },
+ { p5, particles }
+ );
const geo = p5.buildGeometry(() => p5.sphere(5));
p5.background(200);
@@ -1746,28 +1921,12 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
);
});
- visualSuite('2D Shapes', function() {
- visualTest('TRIANGLE_FAN with per-vertex fills', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background(255);
- p5.beginShape(p5.TRIANGLE_FAN);
- p5.fill('red');
- p5.vertex(0, 0);
- const n = 10;
- const r = 20;
- p5.fill('blue');
- for (let i = 0; i <= n; i++) {
- const angle = i/n * p5.TWO_PI;
- p5.vertex(r*p5.cos(angle), r*p5.sin(angle));
- }
- p5.endShape();
- await screenshot();
- });
-
- visualTest('TRIANGLE_FAN in p5.Geometry with per-vertex fills', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background(255);
- const geom = p5.buildGeometry(() => {
+ visualSuite('2D Shapes', function () {
+ visualTest(
+ 'TRIANGLE_FAN with per-vertex fills',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ p5.background(255);
p5.beginShape(p5.TRIANGLE_FAN);
p5.fill('red');
p5.vertex(0, 0);
@@ -1775,32 +1934,42 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
const r = 20;
p5.fill('blue');
for (let i = 0; i <= n; i++) {
- const angle = i/n * p5.TWO_PI;
- p5.vertex(r*p5.cos(angle), r*p5.sin(angle));
+ const angle = (i / n) * p5.TWO_PI;
+ p5.vertex(r * p5.cos(angle), r * p5.sin(angle));
}
p5.endShape();
- });
- p5.model(geom);
- await screenshot();
- });
+ await screenshot();
+ }
+ );
- visualTest('TRIANGLE_STRIP with per-vertex fills', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background(255);
- p5.beginShape(p5.TRIANGLE_STRIP);
- const n = 6;
- for (let i = 0; i < n; i++) {
- p5.fill(i % 2 === 0 ? 'red' : 'blue');
- p5.vertex(p5.map(i, 0, n - 1, -20, 20), i % 2 === 0 ? -10 : 10);
+ visualTest(
+ 'TRIANGLE_FAN in p5.Geometry with per-vertex fills',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ p5.background(255);
+ const geom = p5.buildGeometry(() => {
+ p5.beginShape(p5.TRIANGLE_FAN);
+ p5.fill('red');
+ p5.vertex(0, 0);
+ const n = 10;
+ const r = 20;
+ p5.fill('blue');
+ for (let i = 0; i <= n; i++) {
+ const angle = (i / n) * p5.TWO_PI;
+ p5.vertex(r * p5.cos(angle), r * p5.sin(angle));
+ }
+ p5.endShape();
+ });
+ p5.model(geom);
+ await screenshot();
}
- p5.endShape();
- await screenshot();
- });
+ );
- visualTest('TRIANGLE_STRIP in p5.Geometry with per-vertex fills', async function(p5, screenshot) {
- await p5.createCanvas(50, 50, p5.WEBGPU);
- p5.background(255);
- const geom = p5.buildGeometry(() => {
+ visualTest(
+ 'TRIANGLE_STRIP with per-vertex fills',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ p5.background(255);
p5.beginShape(p5.TRIANGLE_STRIP);
const n = 6;
for (let i = 0; i < n; i++) {
@@ -1808,16 +1977,34 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
p5.vertex(p5.map(i, 0, n - 1, -20, 20), i % 2 === 0 ? -10 : 10);
}
p5.endShape();
- });
- p5.model(geom);
- await screenshot();
- });
+ await screenshot();
+ }
+ );
+
+ visualTest(
+ 'TRIANGLE_STRIP in p5.Geometry with per-vertex fills',
+ async function (p5, screenshot) {
+ await p5.createCanvas(50, 50, p5.WEBGPU);
+ p5.background(255);
+ const geom = p5.buildGeometry(() => {
+ p5.beginShape(p5.TRIANGLE_STRIP);
+ const n = 6;
+ for (let i = 0; i < n; i++) {
+ p5.fill(i % 2 === 0 ? 'red' : 'blue');
+ p5.vertex(p5.map(i, 0, n - 1, -20, 20), i % 2 === 0 ? -10 : 10);
+ }
+ p5.endShape();
+ });
+ p5.model(geom);
+ await screenshot();
+ }
+ );
});
- visualSuite('Feedback', function() {
+ visualSuite('Feedback', function () {
visualTest(
'Drawing accumulates across frames when background is set in setup',
- async function(p5, screenshot) {
+ async function (p5, screenshot) {
await p5.createCanvas(50, 50, p5.WEBGPU);
// Set an initial background before the draw loop starts.
@@ -1826,7 +2013,7 @@ visualTest('randomGaussian() in a fragment loop averages to the mean (WebGPU)',
return new Promise(resolve => {
let frame = 0;
- p5.draw = function() {
+ p5.draw = function () {
// Draw circles without clearing, so they accumulate
p5.noStroke();
p5.fill('blue');
diff --git a/test/unit/visual/visualTest.js b/test/unit/visual/visualTest.js
index b761665d9c..31181d8217 100644
--- a/test/unit/visual/visualTest.js
+++ b/test/unit/visual/visualTest.js
@@ -129,7 +129,7 @@ export function visualSuite(
*/
export function checkMatch(actual, expected, p5) {
- let scale = Math.min(MAX_SIDE/expected.width, MAX_SIDE/expected.height);
+ let scale = Math.min(MAX_SIDE / expected.width, MAX_SIDE / expected.height);
const ratio = expected.width / expected.height;
const narrow = ratio !== 1;
if (narrow) {
@@ -137,10 +137,7 @@ export function checkMatch(actual, expected, p5) {
}
for (const img of [actual, expected]) {
- img.resize(
- Math.ceil(img.width * scale),
- Math.ceil(img.height * scale)
- );
+ img.resize(Math.ceil(img.width * scale), Math.ceil(img.height * scale));
}
// Ensure both images have the same dimensions
@@ -208,8 +205,10 @@ export function checkMatch(actual, expected, p5) {
// Find the connected cluster size using BFS
const clusterSize = findClusterSize(
diffCanvas.pixels,
- x, y,
- width, height,
+ x,
+ y,
+ width,
+ height,
1,
visited
);
@@ -219,16 +218,19 @@ export function checkMatch(actual, expected, p5) {
}
// Define significance thresholds
- const MIN_CLUSTER_SIZE = 4; // Minimum pixels in a significant cluster
- const MAX_TOTAL_DIFF_PIXELS = 40; // Maximum total different pixels
+ const MIN_CLUSTER_SIZE = 4; // Minimum pixels in a significant cluster
+ const MAX_TOTAL_DIFF_PIXELS = 40; // Maximum total different pixels
// Determine if the differences are significant
- const nonLineShiftClusters = clusterSizes
- .filter(c => !c.isLineShift && c.size >= MIN_CLUSTER_SIZE);
+ const nonLineShiftClusters = clusterSizes.filter(
+ c => !c.isLineShift && c.size >= MIN_CLUSTER_SIZE
+ );
// Calculate significant differences excluding line shifts
- const significantDiffPixels = nonLineShiftClusters
- .reduce((sum, c) => sum + c.size, 0);
+ const significantDiffPixels = nonLineShiftClusters.reduce(
+ (sum, c) => sum + c.size,
+ 0
+ );
// Update the diff canvas
diffCanvas.updatePixels();
@@ -238,16 +240,11 @@ export function checkMatch(actual, expected, p5) {
expectedCanvas.remove();
// Determine test result
- const ok = (
+ const ok =
diffCount === 0 ||
- (
- significantDiffPixels === 0 ||
- (
- (significantDiffPixels <= MAX_TOTAL_DIFF_PIXELS) &&
- (nonLineShiftClusters.length <= 2) // Not too many significant clusters
- )
- )
- );
+ significantDiffPixels === 0 ||
+ (significantDiffPixels <= MAX_TOTAL_DIFF_PIXELS &&
+ nonLineShiftClusters.length <= 2); // Not too many significant clusters
return {
ok,
@@ -265,8 +262,10 @@ export function checkMatch(actual, expected, p5) {
*/
function findClusterSize(
pixels,
- startX, startY,
- width, height,
+ startX,
+ startY,
+ width,
+ height,
radius,
visited
) {
@@ -282,11 +281,8 @@ function findClusterSize(
if (visited.has(pos)) continue;
// Skip if not a diff pixel
- if (
- pixels[pos] !== 255 ||
- pixels[pos + 1] !== 0 ||
- pixels[pos + 2] !== 0
- ) continue;
+ if (pixels[pos] !== 255 || pixels[pos + 1] !== 0 || pixels[pos + 2] !== 0)
+ continue;
// Mark as visited
visited.add(pos);
@@ -390,98 +386,104 @@ export function visualTest(
suiteFn = suiteFn.skip;
}
- suiteFn(testName, function() {
+ suiteFn(testName, function () {
let name;
let myp5;
let lastDeviceRatio = window.devicePixelRatio;
- beforeAll(function() {
+ beforeAll(function () {
name = namePrefix + escapeName(testName);
// Force everything to be 1x
window.devicePixelRatio = 1;
return new Promise(res => {
- myp5 = new p5(function(p) {
- p.setup = function() {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
res();
};
});
});
});
- afterAll(function() {
+ afterAll(function () {
window.devicePixelRatio = lastDeviceRatio;
myp5.remove();
});
- test('matches expected screenshots', async function() {
- let expectedScreenshots;
- try {
- const metadata = JSON.parse(await readFile(
- `./test/unit/visual/screenshots/${name}/metadata.json`
- ));
- expectedScreenshots = metadata.numScreenshots;
- } catch (e) {
- console.log(e);
- expectedScreenshots = 0;
- }
+ test(
+ 'matches expected screenshots',
+ async function () {
+ let expectedScreenshots;
+ try {
+ const metadata = JSON.parse(
+ await readFile(
+ `./test/unit/visual/screenshots/${name}/metadata.json`
+ )
+ );
+ expectedScreenshots = metadata.numScreenshots;
+ } catch (e) {
+ console.log(e);
+ expectedScreenshots = 0;
+ }
- const actual = [];
+ const actual = [];
- // Generate screenshots
- await callback(myp5, async () => {
- const img = await myp5.get();
- img.pixelDensity(1);
- actual.push(img);
- });
+ // Generate screenshots
+ await callback(myp5, async () => {
+ const img = await myp5.get();
+ img.pixelDensity(1);
+ actual.push(img);
+ });
+ if (actual.length === 0) {
+ throw new Error(
+ 'No screenshots were generated. Check if your test generates screenshots correctly. If the test includes asynchronous operations, ensure they complete before the test ends.'
+ );
+ }
+ if (expectedScreenshots && actual.length !== expectedScreenshots) {
+ throw new Error(
+ `Expected ${expectedScreenshots} screenshot(s) but generated ${actual.length}`
+ );
+ }
+ if (!expectedScreenshots) {
+ await writeFile(
+ `./test/unit/visual/screenshots/${name}/metadata.json`,
+ JSON.stringify({ numScreenshots: actual.length }, null, 2)
+ );
+ }
- if (actual.length === 0) {
- throw new Error('No screenshots were generated. Check if your test generates screenshots correctly. If the test includes asynchronous operations, ensure they complete before the test ends.');
- }
- if (expectedScreenshots && actual.length !== expectedScreenshots) {
- throw new Error(
- `Expected ${expectedScreenshots} screenshot(s) but generated ${actual.length}`
- );
- }
- if (!expectedScreenshots) {
- await writeFile(
- `./test/unit/visual/screenshots/${name}/metadata.json`,
- JSON.stringify({ numScreenshots: actual.length }, null, 2)
+ const expectedFilenames = actual.map(
+ (_, i) =>
+ `./test/unit/visual/screenshots/${name}/${i.toString().padStart(3, '0')}.png`
);
- }
-
- const expectedFilenames = actual.map(
- (_, i) => `./test/unit/visual/screenshots/${name}/${i.toString().padStart(3, '0')}.png`
- );
- const expected = expectedScreenshots
- ? (
- await Promise.all(
- expectedFilenames.map(path => myp5.loadImage(path.slice(2)))
- )
- )
- : [];
-
- for (let i = 0; i < actual.length; i++) {
- const flatName = name.replace(/\//g, '-');
- const actualFilename = `./test/unit/visual/actual-screenshots/${flatName}-${i.toString().padStart(3, '0')}.png`;
- if (expected[i]) {
- const result = checkMatch(actual[i], expected[i], myp5);
- // Always save the actual image before potentially throwing an error
- writeImageFile(actualFilename, toBase64(actual[i]));
- if (!result.ok) {
- const diffFilename = `./test/unit/visual/actual-screenshots/${flatName}-${i.toString().padStart(3, '0')}-diff.png`;
- writeImageFile(diffFilename, toBase64(result.diff));
- throw new Error(
- `Screenshots do not match! Expected:\n${toBase64(expected[i])}\n\nReceived:\n${toBase64(actual[i])}\n\nDiff:\n${toBase64(result.diff)}\n\n` +
- 'If this is unexpected, paste these URLs into your browser to inspect them.\n\n' +
- `If this change is expected, please delete the screenshots/${name} folder and run tests again to generate a new screenshot.`
- );
+ const expected = expectedScreenshots
+ ? await Promise.all(
+ expectedFilenames.map(path => myp5.loadImage(path.slice(2)))
+ )
+ : [];
+
+ for (let i = 0; i < actual.length; i++) {
+ const flatName = name.replace(/\//g, '-');
+ const actualFilename = `./test/unit/visual/actual-screenshots/${flatName}-${i.toString().padStart(3, '0')}.png`;
+ if (expected[i]) {
+ const result = checkMatch(actual[i], expected[i], myp5);
+ // Always save the actual image before potentially throwing an error
+ writeImageFile(actualFilename, toBase64(actual[i]));
+ if (!result.ok) {
+ const diffFilename = `./test/unit/visual/actual-screenshots/${flatName}-${i.toString().padStart(3, '0')}-diff.png`;
+ writeImageFile(diffFilename, toBase64(result.diff));
+ throw new Error(
+ `Screenshots do not match! Expected:\n${toBase64(expected[i])}\n\nReceived:\n${toBase64(actual[i])}\n\nDiff:\n${toBase64(result.diff)}\n\n` +
+ 'If this is unexpected, paste these URLs into your browser to inspect them.\n\n' +
+ `If this change is expected, please delete the screenshots/${name} folder and run tests again to generate a new screenshot.`
+ );
+ }
+ } else {
+ writeImageFile(expectedFilenames[i], toBase64(actual[i]));
+ writeImageFile(actualFilename, toBase64(actual[i]));
}
- } else {
- writeImageFile(expectedFilenames[i], toBase64(actual[i]));
- writeImageFile(actualFilename, toBase64(actual[i]));
}
- }
- }, timeout);
+ },
+ timeout
+ );
});
}
diff --git a/test/unit/webgl/3d_primitives.js b/test/unit/webgl/3d_primitives.js
index 849c0db326..757b2e7bb7 100644
--- a/test/unit/webgl/3d_primitives.js
+++ b/test/unit/webgl/3d_primitives.js
@@ -1,64 +1,64 @@
import p5 from '../../../src/app.js';
-suite('3D Primitives', function() {
+suite('3D Primitives', function () {
var myp5;
- beforeAll(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, p.WEBGL);
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.prototype.plane', function() {
- test('should be a function', function() {
+ suite('p5.prototype.plane', function () {
+ test('should be a function', function () {
assert.ok(myp5.plane);
assert.typeOf(myp5.plane, 'function');
});
});
- suite('p5.prototype.box', function() {
- test('should be a function', function() {
+ suite('p5.prototype.box', function () {
+ test('should be a function', function () {
assert.ok(myp5.box);
assert.typeOf(myp5.box, 'function');
});
});
- suite('p5.prototype.sphere', function() {
- test('should be a function', function() {
+ suite('p5.prototype.sphere', function () {
+ test('should be a function', function () {
assert.ok(myp5.sphere);
assert.typeOf(myp5.sphere, 'function');
});
});
- suite('p5.prototype.cylinder', function() {
- test('should be a function', function() {
+ suite('p5.prototype.cylinder', function () {
+ test('should be a function', function () {
assert.ok(myp5.cylinder);
assert.typeOf(myp5.cylinder, 'function');
});
});
- suite('p5.prototype.cone', function() {
- test('should be a function', function() {
+ suite('p5.prototype.cone', function () {
+ test('should be a function', function () {
assert.ok(myp5.cone);
assert.typeOf(myp5.cone, 'function');
});
});
- suite('p5.prototype.ellipsoid', function() {
- test('should be a function', function() {
+ suite('p5.prototype.ellipsoid', function () {
+ test('should be a function', function () {
assert.ok(myp5.ellipsoid);
assert.typeOf(myp5.ellipsoid, 'function');
});
});
- suite('p5.prototype.torus', function() {
- test('should be a function', function() {
+ suite('p5.prototype.torus', function () {
+ test('should be a function', function () {
assert.ok(myp5.torus);
assert.typeOf(myp5.torus, 'function');
});
diff --git a/test/unit/webgl/interaction.js b/test/unit/webgl/interaction.js
index f03433d841..4fe6ba4466 100644
--- a/test/unit/webgl/interaction.js
+++ b/test/unit/webgl/interaction.js
@@ -1,36 +1,36 @@
import p5 from '../../../src/app.js';
-suite('Interaction', function() {
+suite('Interaction', function () {
var myp5;
- beforeAll(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, p.WEBGL);
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.prototype.orbitControl', function() {
- test('should be a function', function() {
+ suite('p5.prototype.orbitControl', function () {
+ test('should be a function', function () {
assert.ok(myp5.orbitControl);
assert.typeOf(myp5.orbitControl, 'function');
});
});
- suite('p5.prototype.debugMode', function() {
- test('should be a function', function() {
+ suite('p5.prototype.debugMode', function () {
+ test('should be a function', function () {
assert.ok(myp5.debugMode);
assert.typeOf(myp5.debugMode, 'function');
});
});
- suite('p5.prototype.noDebugMode', function() {
- test('should be a function', function() {
+ suite('p5.prototype.noDebugMode', function () {
+ test('should be a function', function () {
assert.ok(myp5.noDebugMode);
assert.typeOf(myp5.noDebugMode, 'function');
});
diff --git a/test/unit/webgl/light.js b/test/unit/webgl/light.js
index 15fad185e1..7d60804c46 100644
--- a/test/unit/webgl/light.js
+++ b/test/unit/webgl/light.js
@@ -1,22 +1,22 @@
import p5 from '../../../src/app.js';
-suite('light', function() {
+suite('light', function () {
var myp5;
- beforeAll(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, p.WEBGL);
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('Light', function() {
- test('lightFalloff is initialised and set properly', function() {
+ suite('Light', function () {
+ test('lightFalloff is initialised and set properly', function () {
assert.deepEqual(myp5._renderer.states.constantAttenuation, 1);
assert.deepEqual(myp5._renderer.states.linearAttenuation, 0);
assert.deepEqual(myp5._renderer.states.quadraticAttenuation, 0);
@@ -26,7 +26,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.quadraticAttenuation, 4);
});
- test('specularColor is initialised and set properly', function() {
+ test('specularColor is initialised and set properly', function () {
assert.deepEqual(myp5._renderer.states.specularColors, [1, 1, 1]);
assert.deepEqual(myp5._renderer.states.pointLightSpecularColors, []);
assert.deepEqual(
@@ -41,13 +41,12 @@ suite('light', function() {
myp5._renderer.states.pointLightSpecularColors,
[1, 0, 0]
);
- assert.deepEqual(myp5._renderer.states.directionalLightSpecularColors, [
- 1,
- 0,
- 0
- ]);
+ assert.deepEqual(
+ myp5._renderer.states.directionalLightSpecularColors,
+ [1, 0, 0]
+ );
});
- test('noLights works', function() {
+ test('noLights works', function () {
myp5.ambientLight(200, 0, 0);
myp5.pointLight(255, 0, 0, 0, 0, 0);
myp5.directionalLight(255, 0, 0, 0, 0, 0);
@@ -75,14 +74,14 @@ suite('light', function() {
});
});
- suite('spotlight inputs', function() {
+ suite('spotlight inputs', function () {
beforeEach(() => myp5.noLights());
let angle = Math.PI / 4;
let defaultAngle = Math.cos(Math.PI / 3);
let cosAngle = Math.cos(angle);
let conc = 7;
let defaultConc = 100;
- test('default', function() {
+ test('default', function () {
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, []);
assert.deepEqual(myp5._renderer.states.spotLightSpecularColors, []);
assert.deepEqual(myp5._renderer.states.spotLightPositions, []);
@@ -90,7 +89,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, []);
assert.deepEqual(myp5._renderer.states.spotLightConc, []);
});
- test('color,positions,directions', function() {
+ test('color,positions,directions', function () {
let color = myp5.color(255, 0, 255);
let positions = new p5.Vector(1, 2, 3);
let directions = new p5.Vector(0, 1, 0);
@@ -105,7 +104,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [defaultAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('color,positions,directions,angle', function() {
+ test('color,positions,directions,angle', function () {
let color = myp5.color(255, 0, 255);
let positions = new p5.Vector(1, 2, 3);
let directions = new p5.Vector(0, 1, 0);
@@ -120,7 +119,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('color,positions,directions,angle,conc', function() {
+ test('color,positions,directions,angle,conc', function () {
let color = myp5.color(255, 0, 255);
let positions = new p5.Vector(1, 2, 3);
let directions = new p5.Vector(0, 1, 0);
@@ -135,7 +134,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [conc]);
});
- test('c1,c2,c3,positions,directions', function() {
+ test('c1,c2,c3,positions,directions', function () {
let positions = new p5.Vector(1, 2, 3);
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(255, 0, 255, positions, directions);
@@ -149,7 +148,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [defaultAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('color,p1,p2,p3,directions', function() {
+ test('color,p1,p2,p3,directions', function () {
let color = myp5.color(255, 0, 255);
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(color, 1, 2, 3, directions);
@@ -163,7 +162,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [defaultAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('color,positions,r1,r2,r3', function() {
+ test('color,positions,r1,r2,r3', function () {
let color = myp5.color(255, 0, 255);
let positions = new p5.Vector(1, 2, 3);
myp5.spotLight(color, positions, 0, 1, 0);
@@ -177,7 +176,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [defaultAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('c1,c2,c3,positions,directions,angle', function() {
+ test('c1,c2,c3,positions,directions,angle', function () {
let positions = new p5.Vector(1, 2, 3);
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(255, 0, 255, positions, directions, angle);
@@ -191,7 +190,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('color,p1,p2,p3,directions,angle', function() {
+ test('color,p1,p2,p3,directions,angle', function () {
let color = myp5.color(255, 0, 255);
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(color, 1, 2, 3, directions, angle);
@@ -205,7 +204,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('color,positions,r1,r2,r3,angle', function() {
+ test('color,positions,r1,r2,r3,angle', function () {
let color = myp5.color(255, 0, 255);
let positions = new p5.Vector(1, 2, 3);
myp5.spotLight(color, positions, 0, 1, 0, angle);
@@ -219,7 +218,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('c1,c2,c3,positions,directions,angle,conc', function() {
+ test('c1,c2,c3,positions,directions,angle,conc', function () {
let positions = new p5.Vector(1, 2, 3);
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(255, 0, 255, positions, directions, angle, conc);
@@ -233,7 +232,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [7]);
});
- test('color,p1,p2,p3,directions,angle,conc', function() {
+ test('color,p1,p2,p3,directions,angle,conc', function () {
let color = myp5.color(255, 0, 255);
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(color, 1, 2, 3, directions, angle, conc);
@@ -247,7 +246,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [7]);
});
- test('color,positions,r1,r2,r3,angle,conc', function() {
+ test('color,positions,r1,r2,r3,angle,conc', function () {
let color = myp5.color(255, 0, 255);
let positions = new p5.Vector(1, 2, 3);
myp5.spotLight(color, positions, 0, 1, 0, angle, conc);
@@ -261,7 +260,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [7]);
});
- test('c1,c2,c3,p1,p2,p3,directions', function() {
+ test('c1,c2,c3,p1,p2,p3,directions', function () {
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(255, 0, 255, 1, 2, 3, directions);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -274,7 +273,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [defaultAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('c1,c2,c3,positions,r1,r2,r3', function() {
+ test('c1,c2,c3,positions,r1,r2,r3', function () {
let positions = new p5.Vector(1, 2, 3);
myp5.spotLight(255, 0, 255, positions, 0, 1, 0);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -287,7 +286,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [defaultAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('color,p1,p2,p3,r1,r2,r3', function() {
+ test('color,p1,p2,p3,r1,r2,r3', function () {
let color = myp5.color(255, 0, 255);
myp5.spotLight(color, 1, 2, 3, 0, 1, 0);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -300,7 +299,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [defaultAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('c1,c2,c3,p1,p2,p3,directions,angle', function() {
+ test('c1,c2,c3,p1,p2,p3,directions,angle', function () {
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(255, 0, 255, 1, 2, 3, directions, angle);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -313,7 +312,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('c1,c2,c3,positions,r1,r2,r3,angle', function() {
+ test('c1,c2,c3,positions,r1,r2,r3,angle', function () {
let positions = new p5.Vector(1, 2, 3);
myp5.spotLight(255, 0, 255, positions, 0, 1, 0, angle);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -326,7 +325,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('color,p1,p2,p3,r1,r2,r3,angle', function() {
+ test('color,p1,p2,p3,r1,r2,r3,angle', function () {
let color = myp5.color(255, 0, 255);
myp5.spotLight(color, 1, 2, 3, 0, 1, 0, angle);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -339,7 +338,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('c1,c2,c3,p1,p2,p3,directions,angle,conc', function() {
+ test('c1,c2,c3,p1,p2,p3,directions,angle,conc', function () {
let directions = new p5.Vector(0, 1, 0);
myp5.spotLight(255, 0, 255, 1, 2, 3, directions, angle, conc);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -352,7 +351,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [7]);
});
- test('c1,c2,c3,positions,r1,r2,r3,angle,conc', function() {
+ test('c1,c2,c3,positions,r1,r2,r3,angle,conc', function () {
let positions = new p5.Vector(1, 2, 3);
myp5.spotLight(255, 0, 255, positions, 0, 1, 0, angle, conc);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -365,7 +364,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [7]);
});
- test('color,p1,p2,p3,r1,r2,r3,angle,conc', function() {
+ test('color,p1,p2,p3,r1,r2,r3,angle,conc', function () {
let color = myp5.color(255, 0, 255);
myp5.spotLight(color, 1, 2, 3, 0, 1, 0, angle, conc);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
@@ -378,7 +377,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [7]);
});
- test('c1,c2,c3,p1,p2,p3,r1,r2,r3', function() {
+ test('c1,c2,c3,p1,p2,p3,r1,r2,r3', function () {
myp5.spotLight(255, 0, 255, 1, 2, 3, 0, 1, 0);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
assert.deepEqual(
@@ -390,7 +389,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [defaultAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('c1,c2,c3,p1,p2,p3,r1,r2,r3,angle', function() {
+ test('c1,c2,c3,p1,p2,p3,r1,r2,r3,angle', function () {
myp5.spotLight(255, 0, 255, 1, 2, 3, 0, 1, 0, angle);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
assert.deepEqual(
@@ -402,7 +401,7 @@ suite('light', function() {
assert.deepEqual(myp5._renderer.states.spotLightAngle, [cosAngle]);
assert.deepEqual(myp5._renderer.states.spotLightConc, [defaultConc]);
});
- test('c1,c2,c3,p1,p2,p3,r1,r2,r3,angle,conc', function() {
+ test('c1,c2,c3,p1,p2,p3,r1,r2,r3,angle,conc', function () {
myp5.spotLight(255, 0, 255, 1, 2, 3, 0, 1, 0, angle, 7);
assert.deepEqual(myp5._renderer.states.spotLightDiffuseColors, [1, 0, 1]);
assert.deepEqual(
diff --git a/test/unit/webgl/normal.js b/test/unit/webgl/normal.js
index cbb7870f70..41af537049 100644
--- a/test/unit/webgl/normal.js
+++ b/test/unit/webgl/normal.js
@@ -1,37 +1,37 @@
import p5 from '../../../src/app.js';
-suite('', function() {
+suite('', function () {
var myp5;
- beforeAll(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, p.WEBGL);
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('p5.prototype.normal', function() {
- test('should be a function', function() {
+ suite('p5.prototype.normal', function () {
+ test('should be a function', function () {
assert.ok(myp5.normal);
assert.typeOf(myp5.normal, 'function');
});
- test('accepts numeric arguments', function() {
+ test('accepts numeric arguments', function () {
assert.doesNotThrow(
- function() {
+ function () {
myp5.normal(0, 1, 0);
},
Error,
'got unwanted exception'
);
});
- test('accepts vector argument', function() {
+ test('accepts vector argument', function () {
assert.doesNotThrow(
- function() {
+ function () {
myp5.normal(myp5.createVector(0, 1, 0));
},
Error,
diff --git a/test/unit/webgl/p5.Camera.js b/test/unit/webgl/p5.Camera.js
index 92cfe69962..b006cc5413 100644
--- a/test/unit/webgl/p5.Camera.js
+++ b/test/unit/webgl/p5.Camera.js
@@ -2,13 +2,13 @@ import p5 from '../../../src/app.js';
import { HALF_PI } from '../../../src/core/constants';
import '../../js/chai_helpers';
-suite('p5.Camera', function() {
+suite('p5.Camera', function () {
var myp5;
var myCam;
var delta = 0.001;
// returns values to test which have changed
- var getVals = function(cam) {
+ var getVals = function (cam) {
return {
ex: cam.eyeX,
ey: cam.eyeY,
@@ -22,20 +22,20 @@ suite('p5.Camera', function() {
};
};
- beforeAll(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {
+ beforeAll(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, p.WEBGL);
myCam = p.createCamera();
};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- beforeEach(function() {
+ beforeEach(function () {
myp5.angleMode(myp5.RADIANS);
// set camera defaults below according to current default values
@@ -43,7 +43,7 @@ suite('p5.Camera', function() {
myCam.camera(
0,
0,
- 100 / 2.0 / Math.tan(Math.PI * 30.0 / 180.0),
+ 100 / 2.0 / Math.tan((Math.PI * 30.0) / 180.0),
0,
0,
0,
@@ -51,43 +51,37 @@ suite('p5.Camera', function() {
1,
0
);
- myCam.perspective(
- Math.PI / 3.0,
- 1,
- myCam.eyeZ / 10.0,
- myCam.eyeZ * 10.0
- );
+ myCam.perspective(Math.PI / 3.0, 1, myCam.eyeZ / 10.0, myCam.eyeZ * 10.0);
myp5.setCamera(myCam);
});
- suite('createCamera()', function() {
- test('creates a p5.Camera object', function() {
+ suite('createCamera()', function () {
+ test('creates a p5.Camera object', function () {
var myCam2 = myp5.createCamera();
assert.instanceOf(myCam2, p5.Camera);
});
- test('createCamera does not immediately attach to renderer', function() {
+ test('createCamera does not immediately attach to renderer', function () {
var myCam2 = myp5.createCamera();
assert.notEqual(myCam2, myp5._renderer.states.curCamera);
});
- test('setCamera() attaches a camera to renderer', function() {
+ test('setCamera() attaches a camera to renderer', function () {
var myCam2 = myp5.createCamera();
myp5.setCamera(myCam2);
assert.equal(myCam2, myp5._renderer.states.curCamera);
});
});
- suite('Rotation', function() {
+ suite('Rotation', function () {
test('Pan() with positive parameter sets correct matrix w/o changing\
- eyeXYZ or upXYZ', function() {
+ eyeXYZ or upXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 0.5403022766113281, -0, 0.8414710164070129, 0,
- 0, 1, 0, 0,
- -0.8414710164070129, 0, 0.5403022766113281, 0,
- 72.87352752685547, 0, -46.79154968261719, 1
+ 0.5403022766113281, -0, 0.8414710164070129, 0, 0, 1, 0, 0,
+ -0.8414710164070129, 0, 0.5403022766113281, 0, 72.87352752685547, 0,
+ -46.79154968261719, 1
]);
myCam.pan(1);
@@ -104,14 +98,13 @@ suite('p5.Camera', function() {
});
test('Pan() with negative parameter sets correct matrix w/o changing\
- eyeXYZ or upXYZ', function() {
+ eyeXYZ or upXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 0.5403022766113281, 0, -0.8414710164070129, 0,
- -0, 1, 0, 0,
- 0.8414710164070129, 0, 0.5403022766113281, 0,
- -72.87352752685547, 0, -46.79154968261719, 1
+ 0.5403022766113281, 0, -0.8414710164070129, 0, -0, 1, 0, 0,
+ 0.8414710164070129, 0, 0.5403022766113281, 0, -72.87352752685547, 0,
+ -46.79154968261719, 1
]);
myCam.pan(-1);
@@ -126,14 +119,12 @@ suite('p5.Camera', function() {
assert.strictEqual(myCam.upY, orig.uy, 'up Y pos changed');
assert.strictEqual(myCam.upZ, orig.uz, 'up Z pos changed');
});
- test('Pan(0) sets correct matrix w/o changing eyeXYZ or upXYZ', function() {
+ test('Pan(0) sets correct matrix w/o changing eyeXYZ or upXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -86.6025390625, 1]);
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -86.6025390625, 1
+ ]);
myCam.pan(0);
@@ -149,14 +140,13 @@ suite('p5.Camera', function() {
});
test('Tilt() with positive parameter sets correct Matrix w/o \
- changing eyeXYZ', function() {
+ changing eyeXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 0.07073719799518585, -0.9974949955940247, 0,
- -0, 0.9974949955940247, 0.07073719799518585, 0,
- 0, -86.3855972290039, -6.126020908355713, 1
+ 1, 0, 0, 0, 0, 0.07073719799518585, -0.9974949955940247, 0, -0,
+ 0.9974949955940247, 0.07073719799518585, 0, 0, -86.3855972290039,
+ -6.126020908355713, 1
]);
myCam.tilt(1.5);
@@ -169,14 +159,13 @@ suite('p5.Camera', function() {
});
test('Tilt() with negative parameter sets correct matrix w/o \
- changing eyeXYZ', function() {
+ changing eyeXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 0.07073719799518585, 0.9974949955940247, 0,
- 0, -0.9974949955940247, 0.07073719799518585, 0,
- 0, 86.3855972290039, -6.126020908355713, 1
+ 1, 0, 0, 0, 0, 0.07073719799518585, 0.9974949955940247, 0, 0,
+ -0.9974949955940247, 0.07073719799518585, 0, 0, 86.3855972290039,
+ -6.126020908355713, 1
]);
myCam.tilt(-1.5);
@@ -187,14 +176,11 @@ suite('p5.Camera', function() {
assert.strictEqual(myCam.eyeY, orig.ey, 'eye Y pos changed');
assert.strictEqual(myCam.eyeZ, orig.ez, 'eye Z pos changed');
});
- test('Tilt(0) sets correct matrix w/o changing eyeXYZ', function() {
+ test('Tilt(0) sets correct matrix w/o changing eyeXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -86.6025390625, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -86.6025390625, 1
]);
myCam.tilt(0);
@@ -207,14 +193,11 @@ suite('p5.Camera', function() {
});
test('Roll() with positive parameter sets correct Matrix w/o \
- changing eyeXYZ', function() {
+ changing eyeXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 0, -1, 0, 0,
- 1, 0, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -86.6025390625, 1
+ 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, -86.6025390625, 1
]);
myCam.roll(HALF_PI);
@@ -227,14 +210,11 @@ suite('p5.Camera', function() {
});
test('Roll() with negative parameter sets correct matrix w/o \
- changing eyeXYZ', function() {
+ changing eyeXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 0, -1, 0, 0,
- 1, 0, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -86.6025390625, 1
+ 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, -86.6025390625, 1
]);
myCam.roll(HALF_PI);
@@ -246,14 +226,11 @@ suite('p5.Camera', function() {
assert.strictEqual(myCam.eyeZ, orig.ez, 'eye Z pos changed');
});
- test('Roll(0) sets correct matrix w/o changing upXYZ and eyeXYZ', function() {
+ test('Roll(0) sets correct matrix w/o changing upXYZ and eyeXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -86.6025390625, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -86.6025390625, 1
]);
myCam.roll(0);
@@ -270,7 +247,7 @@ suite('p5.Camera', function() {
});
test('LookAt() should set centerXYZ without changing eyeXYZ or \
- upXYZ', function() {
+ upXYZ', function () {
var orig = getVals(myCam);
myCam.lookAt(10, 20, 30);
@@ -289,28 +266,27 @@ suite('p5.Camera', function() {
});
});
- suite('Rotation with angleMode(DEGREES)', function() {
- beforeEach(function() {
+ suite('Rotation with angleMode(DEGREES)', function () {
+ beforeEach(function () {
myp5.push();
myp5.angleMode(myp5.DEGREES);
});
- afterEach(function() {
+ afterEach(function () {
myp5.pop();
});
test('Pan() with positive parameter sets correct matrix w/o changing\
- eyeXYZ or upXYZ', function() {
+ eyeXYZ or upXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 0.5403022766113281, -0, 0.8414710164070129, 0,
- 0, 1, 0, 0,
- -0.8414710164070129, 0, 0.5403022766113281, 0,
- 72.87352752685547, 0, -46.79154968261719, 1
+ 0.5403022766113281, -0, 0.8414710164070129, 0, 0, 1, 0, 0,
+ -0.8414710164070129, 0, 0.5403022766113281, 0, 72.87352752685547, 0,
+ -46.79154968261719, 1
]);
- myCam.pan(1 * 180 / Math.PI);
+ myCam.pan((1 * 180) / Math.PI);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
@@ -324,16 +300,16 @@ suite('p5.Camera', function() {
});
test('Tilt() with positive parameter sets correct Matrix w/o \
- changing eyeXYZ', function() {
+ changing eyeXYZ', function () {
var orig = getVals(myCam);
- var expectedMatrix = new Float32Array(
- [1, 0, 0, 0,
- 0, 0.07073719799518585, -0.9974949955940247, 0,
- -0, 0.9974949955940247, 0.07073719799518585, 0,
- 0, -86.3855972290039, -6.126020908355713, 1]);
+ var expectedMatrix = new Float32Array([
+ 1, 0, 0, 0, 0, 0.07073719799518585, -0.9974949955940247, 0, -0,
+ 0.9974949955940247, 0.07073719799518585, 0, 0, -86.3855972290039,
+ -6.126020908355713, 1
+ ]);
- myCam.tilt(1.5 * 180 / Math.PI);
+ myCam.tilt((1.5 * 180) / Math.PI);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
@@ -343,14 +319,11 @@ suite('p5.Camera', function() {
});
test('Roll() with positive parameter sets correct Matrix w/o \
- changing eyeXYZ', function() {
+ changing eyeXYZ', function () {
var orig = getVals(myCam);
var expectedMatrix = new Float32Array([
- 0, -1, 0, 0,
- 1, 0, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -86.6025390625, 1
+ 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, -86.6025390625, 1
]);
myCam.roll(90);
@@ -363,10 +336,10 @@ suite('p5.Camera', function() {
});
});
- suite('Position / Orientation', function() {
- suite('Camera()', function() {
+ suite('Position / Orientation', function () {
+ suite('Camera()', function () {
test('Camera() with positive parameters sets eye, center, and \
- up XYZ properties', function() {
+ up XYZ properties', function () {
myCam.camera(1, 2, 3, 4, 5, 6, 0, 1, 0);
assert.strictEqual(myCam.eyeX, 1);
@@ -382,7 +355,7 @@ suite('p5.Camera', function() {
assert.strictEqual(myCam.upZ, 0);
});
test('Camera() with negative parameters sets eye, center, and \
- up XYZ properties', function() {
+ up XYZ properties', function () {
myCam.camera(-1, -2, -3, -4, -5, -6, 0, -1, 0);
assert.strictEqual(myCam.eyeX, -1);
@@ -399,72 +372,54 @@ suite('p5.Camera', function() {
});
});
- test('Move() with positive parameters sets correct matrix', function() {
+ test('Move() with positive parameters sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- -1, -2, -89.6025390625, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1, -2, -89.6025390625, 1
]);
myCam.move(1, 2, 3);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('Move() with negative parameters sets correct matrix', function() {
+ test('Move() with negative parameters sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 1, 2, -83.6025390625, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, -83.6025390625, 1
]);
myCam.move(-1, -2, -3);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('Move(0,0,0) sets correct matrix', function() {
+ test('Move(0,0,0) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -86.6025390625, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -86.6025390625, 1
]);
myCam.move(0, 0, 0);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('SetPosition() with positive parameters sets correct matrix', function() {
+ test('SetPosition() with positive parameters sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- -1, -2, -3, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1, -2, -3, 1
]);
myCam.setPosition(1, 2, 3);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('SetPosition() with negative parameters sets correct matrix', function() {
+ test('SetPosition() with negative parameters sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 1, 2, 3, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1
]);
myCam.setPosition(-1, -2, -3);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('SetPosition(0,0,0) sets correct matrix', function() {
+ test('SetPosition(0,0,0) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
]);
myCam.setPosition(0, 0, 0);
@@ -472,7 +427,7 @@ suite('p5.Camera', function() {
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('test for matrix manipulation with set()', function() {
+ test('test for matrix manipulation with set()', function () {
myCam = myp5.createCamera();
myp5.setCamera(myCam);
const copyCam = myCam.copy();
@@ -495,86 +450,80 @@ suite('p5.Camera', function() {
);
});
- test('_orbit(1,0,0) sets correct matrix', function() {
+ test('_orbit(1,0,0) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 0.5403022766113281, -5.1525235865883254e-17, 0.8414709568023682, 0,
- 0, 1, 6.123234262925839e-17, 0,
- -0.8414709568023682, -3.3083975336675e-17, 0.5403022766113281, 0,
- 8.216248374992574e-7, -7.180680227154989e-23, -86.6025390625, 1
+ 0.5403022766113281, -5.1525235865883254e-17, 0.8414709568023682, 0, 0,
+ 1, 6.123234262925839e-17, 0, -0.8414709568023682, -3.3083975336675e-17,
+ 0.5403022766113281, 0, 8.216248374992574e-7, -7.180680227154989e-23,
+ -86.6025390625, 1
]);
myCam._orbit(1, 0, 0);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('_orbit(0,1,0) sets correct matrix', function() {
+ test('_orbit(0,1,0) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 0.5403022766113281, -0.8414709568023682, 0,
- -0, 0.8414709568023682, 0.5403022766113281, 0,
- 0, -8.216248374992574e-7, -86.6025390625, 1
+ 1, 0, 0, 0, 0, 0.5403022766113281, -0.8414709568023682, 0, -0,
+ 0.8414709568023682, 0.5403022766113281, 0, 0, -8.216248374992574e-7,
+ -86.6025390625, 1
]);
myCam._orbit(0, 1, 0);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('_orbit(0,0,1) sets correct matrix', function() {
+ test('_orbit(0,0,1) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 6.123234262925839e-17, 0,
- 0, -6.123234262925839e-17, 1, 0,
- 0, 2.3139252783628976e-21, -866.025390625, 1
+ 1, 0, 0, 0, 0, 1, 6.123234262925839e-17, 0, 0, -6.123234262925839e-17,
+ 1, 0, 0, 2.3139252783628976e-21, -866.025390625, 1
]);
myCam._orbit(0, 0, 1);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('_orbit(-1,0,0) sets correct matrix', function() {
+ test('_orbit(-1,0,0) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 0.5403022766113281, 5.1525235865883254e-17, -0.8414709568023682, 0,
- -0, 1, 6.123234262925839e-17, 0,
- 0.8414709568023682, -3.3083975336675e-17, 0.5403022766113281, 0,
- -8.216248374992574e-7, -7.180680227154989e-23, -86.6025390625, 1
+ 0.5403022766113281, 5.1525235865883254e-17, -0.8414709568023682, 0, -0,
+ 1, 6.123234262925839e-17, 0, 0.8414709568023682, -3.3083975336675e-17,
+ 0.5403022766113281, 0, -8.216248374992574e-7, -7.180680227154989e-23,
+ -86.6025390625, 1
]);
myCam._orbit(-1, 0, 0);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('_orbit(0,-1,0) sets correct matrix', function() {
+ test('_orbit(0,-1,0) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 0.5403022766113281, 0.8414709568023682, 0,
- 0, -0.8414709568023682, 0.5403022766113281, 0,
- 0, 8.216248374992574e-7, -86.6025390625, 1
+ 1, 0, 0, 0, 0, 0.5403022766113281, 0.8414709568023682, 0, 0,
+ -0.8414709568023682, 0.5403022766113281, 0, 0, 8.216248374992574e-7,
+ -86.6025390625, 1
]);
myCam._orbit(0, -1, 0);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('_orbit(0,0,-1) sets correct matrix', function() {
+ test('_orbit(0,0,-1) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 6.123234262925839e-17, 0,
- 0, -6.123234262925839e-17, 1, 0,
- 0, 2.3139253036064466e-23, -8.66025447845459, 1
+ 1, 0, 0, 0, 0, 1, 6.123234262925839e-17, 0, 0, -6.123234262925839e-17,
+ 1, 0, 0, 2.3139253036064466e-23, -8.66025447845459, 1
]);
myCam._orbit(0, 0, -1);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('_orbit() does not force up vector to be parallel to y-axis', function() {
+ test('_orbit() does not force up vector to be parallel to y-axis', function () {
// Check the shape of the camera matrix to make sure that the up vector
// does not become (0,1,0) or (0,-1,0) after running _orbit()
var expectedMatrix = new Float32Array([
-0.2129584103822708, 0.9760860204696655, -0.04364387318491936, 0,
- -0.9770612716674805, -0.21274586021900177, 0.009512536227703094, 0,
- -0, 0.04466851428151131, 0.9990018606185913, 0,
- 2.445493976210855e-7, -1.4983223195486062e-7, -866.025390625, 1
+ -0.9770612716674805, -0.21274586021900177, 0.009512536227703094, 0, -0,
+ 0.04466851428151131, 0.9990018606185913, 0, 2.445493976210855e-7,
+ -1.4983223195486062e-7, -866.025390625, 1
]);
myCam.camera(100, 100, 100, 0, 0, 0, 0, 0, -1);
@@ -582,7 +531,7 @@ suite('p5.Camera', function() {
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('up vector of an arbitrary direction reverses by _orbit(0,PI,0)', function() {
+ test('up vector of an arbitrary direction reverses by _orbit(0,PI,0)', function () {
// Make sure the up vector is reversed by doing _orbit(0, Math.PI, 0)
myCam.camera(100, 100, 100, 0, 0, 0, 1, 2, 3);
const prevUpX = myCam.upX;
@@ -597,7 +546,7 @@ suite('p5.Camera', function() {
expect(currUpY).to.be.closeTo(-prevUpY, 0.001);
expect(currUpZ).to.be.closeTo(-prevUpZ, 0.001);
});
- test('_orbit() ensures myCam.upY switches direction (from 1 to -1) at camPhi <= 0', function() {
+ test('_orbit() ensures myCam.upY switches direction (from 1 to -1) at camPhi <= 0', function () {
// the following should produce the upY with inverted direction(from 1 to -1)
// when camPhi changes from positive to negative or zero
myCam._orbit(0, -Math.PI, 0);
@@ -605,7 +554,7 @@ suite('p5.Camera', function() {
// myCam.upY should be -1
assert.equal(myCam.upY, -1);
});
- test('_orbit() ensures myCam.upY switches direction (from -1 to 1) at camPhi <= 0', function() {
+ test('_orbit() ensures myCam.upY switches direction (from -1 to 1) at camPhi <= 0', function () {
// the following should produce the upY with inverted direction(from -1 to 1)
// when camPhi changes from negative to positive or zero
myCam._orbit(0, -Math.PI, 0);
@@ -614,7 +563,7 @@ suite('p5.Camera', function() {
// myCam.upY should be 1
assert.equal(myCam.upY, 1);
});
- test('_orbit() ensures myCam.upY switches direction (from 1 to -1) at camPhi >= PI', function() {
+ test('_orbit() ensures myCam.upY switches direction (from 1 to -1) at camPhi >= PI', function () {
// the following should produce the upY with inverted direction(from 1 to -1)
// when camPhi reaches PI
myCam._orbit(0, Math.PI, 0);
@@ -622,7 +571,7 @@ suite('p5.Camera', function() {
// myCam.upY should be -1
assert.equal(myCam.upY, -1);
});
- test('_orbit() ensures myCam.upY switches direction (from -1 to 1) at camPhi >= PI', function() {
+ test('_orbit() ensures myCam.upY switches direction (from -1 to 1) at camPhi >= PI', function () {
// the following should produce the upY with inverted direction(from -1 to 1)
// when camPhi reaches PI
myCam._orbit(0, Math.PI, 0);
@@ -631,19 +580,20 @@ suite('p5.Camera', function() {
// myCam.upY should be 1
assert.equal(myCam.upY, 1);
});
- test('_orbit() ensures camera can do multiple continuous 360deg rotations', function() {
+ test('_orbit() ensures camera can do multiple continuous 360deg rotations', function () {
// the following should produce two camera objects having same properties.
myCam._orbit(0, Math.PI, 0);
var myCamCopy = myCam.copy();
myCamCopy._orbit(0, Math.PI, 0);
myCamCopy._orbit(0, Math.PI, 0);
for (let i = 0; i < myCamCopy.cameraMatrix.mat4.length; i++) {
- expect(
- myCamCopy.cameraMatrix.mat4[i]).to.be.closeTo(
- myCam.cameraMatrix.mat4[i], 0.001);
+ expect(myCamCopy.cameraMatrix.mat4[i]).to.be.closeTo(
+ myCam.cameraMatrix.mat4[i],
+ 0.001
+ );
}
});
- test('Returns to the origin after a 360° rotation regardless of the up vector', function() {
+ test('Returns to the origin after a 360° rotation regardless of the up vector', function () {
// Even if the up vector is not (0,1,0) or (0,-1,0), _orbit() makes sure that
// the camera returns to its original position when rotated 360 degrees vertically.
myCam.camera(100, 100, 100, 0, 0, 0, 1, 2, 3);
@@ -653,32 +603,37 @@ suite('p5.Camera', function() {
myCamCopy._orbit(0, Math.PI * 0.01, 0);
}
for (let i = 0; i < myCamCopy.cameraMatrix.mat4.length; i++) {
- expect(
- myCamCopy.cameraMatrix.mat4[i]).to.be.closeTo(
- myCam.cameraMatrix.mat4[i], 0.001);
+ expect(myCamCopy.cameraMatrix.mat4[i]).to.be.closeTo(
+ myCam.cameraMatrix.mat4[i],
+ 0.001
+ );
}
});
- test('_orbit() ensures radius > 0', function() {
+ test('_orbit() ensures radius > 0', function () {
// the following should produce two camera objects having same properties.
myCam._orbit(0, Math.PI, 0);
var myCamCopy = myCam.copy();
myCamCopy._orbit(0, 0, -100);
myCam._orbit(0, 0, -250);
- assert.deepCloseTo(myCam.cameraMatrix.mat4, myCamCopy.cameraMatrix.mat4, 4, 'deep equal is failing');
+ assert.deepCloseTo(
+ myCam.cameraMatrix.mat4,
+ myCamCopy.cameraMatrix.mat4,
+ 4,
+ 'deep equal is failing'
+ );
});
- test('_orbitFree(1,0,0) sets correct matrix', function() {
+ test('_orbitFree(1,0,0) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 0.5403022766113281, 0, -0.8414709568023682, 0,
- -0, 1, 0, 0,
- 0.8414709568023682, 0, 0.5403022766113281, 0,
- -8.216248374992574e-7, 0, -86.6025390625, 1
+ 0.5403022766113281, 0, -0.8414709568023682, 0, -0, 1, 0, 0,
+ 0.8414709568023682, 0, 0.5403022766113281, 0, -8.216248374992574e-7, 0,
+ -86.6025390625, 1
]);
myCam._orbitFree(1, 0, 0);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('_orbitFree(0,1,0) sets correct matrix', function() {
+ test('_orbitFree(0,1,0) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
1, -2.8148363983860944e-17, -5.1525235865883254e-17, 0,
-2.8148363983860944e-17, 0.5403022766113281, -0.8414709568023682, 0,
@@ -690,19 +645,16 @@ suite('p5.Camera', function() {
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('_orbitFree(0,0,1) sets correct matrix', function() {
+ test('_orbitFree(0,0,1) sets correct matrix', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -866.025390625, 1
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -866.025390625, 1
]);
myCam._orbitFree(0, 0, 1);
assert.deepCloseTo(myCam.cameraMatrix.mat4, expectedMatrix);
});
- test('Rotate camera 360° with _orbitFree() returns it to its original position', function() {
+ test('Rotate camera 360° with _orbitFree() returns it to its original position', function () {
// Rotate the camera 360 degrees in any direction using _orbitFree()
// and it will return to its original state.
myCam.camera(100, 100, 100, 0, 0, 0, 1, 2, 3);
@@ -713,16 +665,17 @@ suite('p5.Camera', function() {
myCamCopy._orbitFree(Math.PI * 0.006, Math.PI * 0.008, 0);
}
for (let i = 0; i < myCamCopy.cameraMatrix.mat4.length; i++) {
- expect(
- myCamCopy.cameraMatrix.mat4[i]).to.be.closeTo(
- myCam.cameraMatrix.mat4[i], 0.001);
+ expect(myCamCopy.cameraMatrix.mat4[i]).to.be.closeTo(
+ myCam.cameraMatrix.mat4[i],
+ 0.001
+ );
}
});
});
- suite('Projection', function() {
- suite('ortho()', function() {
- test('ortho() sets renderer uPMatrix', function() {
+ suite('Projection', function () {
+ suite('ortho()', function () {
+ test('ortho() sets renderer uPMatrix', function () {
myCam.ortho(-10, 10, -10, 10, 0, 100);
assert.deepCloseTo(
@@ -731,41 +684,35 @@ suite('p5.Camera', function() {
);
});
- test('ortho() sets projection matrix correctly', function() {
+ test('ortho() sets projection matrix correctly', function () {
// expectedMatrix array needs to match Float32Array type of
// p5.Camera projMatrix's mat4 array for deepCloseTo to work
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, -1, 0, 0,
- 0, 0, -1, 0,
- -0, -0, -1, 1
+ 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, -0, -0, -1, 1
]);
-
myCam.ortho(-1, 1, -1, 1, 0, 2);
assert.deepCloseTo(myCam.projMatrix.mat4, expectedMatrix);
});
- test('ortho() with no parameters specified (sets default with added far)', function() {
+ test('ortho() with no parameters specified (sets default with added far)', function () {
var expectedMatrix = new Float32Array([
- 0.019999999552965164, 0, 0, 0,
- 0, -0.019999999552965164, 0, 0,
- 0, 0, -0.002222222276031971, 0,
- -0, -0, -1, 1
+ 0.019999999552965164, 0, 0, 0, 0, -0.019999999552965164, 0, 0, 0, 0,
+ -0.002222222276031971, 0, -0, -0, -1, 1
]);
myCam.ortho();
assert.deepCloseTo(myCam.projMatrix.mat4, expectedMatrix);
});
- test('ortho() sets cameraType to custom', function() {
+ test('ortho() sets cameraType to custom', function () {
myCam.ortho();
assert.equal(myCam.cameraType, 'custom');
});
});
- suite('perspective()', function() {
- test('perspective() sets renderer uPMatrix', function() {
+ suite('perspective()', function () {
+ test('perspective() sets renderer uPMatrix', function () {
myCam.perspective(Math.PI / 3.0, 1, 1, 100);
assert.deepCloseTo(
@@ -773,12 +720,9 @@ suite('p5.Camera', function() {
myp5._renderer.states.uPMatrix.mat4
);
});
- test('perspective() sets projection matrix correctly', function() {
+ test('perspective() sets projection matrix correctly', function () {
var expectedMatrix = new Float32Array([
- 1, 0, 0, 0,
- 0, -1, 0, 0,
- 0, 0, -3, -1,
- 0, 0, -40, 0
+ 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -3, -1, 0, 0, -40, 0
]);
myCam.perspective(Math.PI / 2, 1, 10, 20);
@@ -786,12 +730,10 @@ suite('p5.Camera', function() {
assert.deepCloseTo(myCam.projMatrix.mat4, expectedMatrix);
});
- test('perspective() with no parameters specified (sets default)', function() {
+ test('perspective() with no parameters specified (sets default)', function () {
var expectedMatrix = new Float32Array([
- 16,0,0,0,
- 0,-16,0,0,
- 0,0,-1.0202020406723022,-1,
- 0,0,-161.6161651611328,0
+ 16, 0, 0, 0, 0, -16, 0, 0, 0, 0, -1.0202020406723022, -1, 0, 0,
+ -161.6161651611328, 0
]);
myCam.perspective();
@@ -799,13 +741,13 @@ suite('p5.Camera', function() {
assert.deepCloseTo(myCam.projMatrix.mat4, expectedMatrix);
});
- test('perspective() with no parameters sets cameraType to default', function() {
+ test('perspective() with no parameters sets cameraType to default', function () {
myCam.perspective();
assert.equal(myCam.cameraType, 'default');
});
});
- suite('frustum()', function() {
- test('frustum() sets renderer uPMatrix', function() {
+ suite('frustum()', function () {
+ test('frustum() sets renderer uPMatrix', function () {
myCam.frustum(-10, 10, -20, 20, -100, 100);
assert.deepCloseTo(
@@ -813,27 +755,20 @@ suite('p5.Camera', function() {
myp5._renderer.states.uPMatrix.mat4
);
});
- test('frustum() sets projection matrix correctly', function() {
-
+ test('frustum() sets projection matrix correctly', function () {
var expectedMatrix = new Float32Array([
- -2, 0, 0, 0,
- 0, 2, 0, 0,
- 0, 0, -0, -1,
- 0, 0, 2, 0
+ -2, 0, 0, 0, 0, 2, 0, 0, 0, 0, -0, -1, 0, 0, 2, 0
]);
-
myCam.frustum(-1, 1, -1, 1, -2, 2);
assert.deepCloseTo(myCam.projMatrix.mat4, expectedMatrix);
});
- test('frustum() with no parameters specified (sets default)', function() {
+ test('frustum() with no parameters specified (sets default)', function () {
var expectedMatrix = new Float32Array([
- 16,0,0,0,
- 0,16,0,0,
- 0,-0,-1.0202020406723022,-1,
- 0,0,-161.6161651611328,0
+ 16, 0, 0, 0, 0, 16, 0, 0, 0, -0, -1.0202020406723022, -1, 0, 0,
+ -161.6161651611328, 0
]);
myCam.frustum();
@@ -841,21 +776,23 @@ suite('p5.Camera', function() {
assert.deepCloseTo(myCam.projMatrix.mat4, expectedMatrix);
});
- test('frustum() sets cameraType to custom', function() {
+ test('frustum() sets cameraType to custom', function () {
myCam.frustum(-1, 1, -1, 1, -2, 2);
assert.equal(myCam.cameraType, 'custom');
});
});
});
- suite('slerp()', function() {
- const expectCameraMatricesAreClose = function(cam0, cam1) {
+ suite('slerp()', function () {
+ const expectCameraMatricesAreClose = function (cam0, cam1) {
for (let i = 0; i < cam0.cameraMatrix.mat4.length; i++) {
- expect(cam0.cameraMatrix.mat4[i])
- .to.be.closeTo(cam1.cameraMatrix.mat4[i], 0.001);
+ expect(cam0.cameraMatrix.mat4[i]).to.be.closeTo(
+ cam1.cameraMatrix.mat4[i],
+ 0.001
+ );
}
};
- test('if amt is 0 or 1, the argument camera is set', function() {
+ test('if amt is 0 or 1, the argument camera is set', function () {
myCam = myp5.createCamera();
const cam0 = myCam.copy();
cam0.camera(20, 30, 40, 2, 3, 8, 0, 9, -6);
@@ -870,7 +807,7 @@ suite('p5.Camera', function() {
myCam.slerp(cam0, cam1, 1);
assert.deepCloseTo(myCam.cameraMatrix.mat4, cam1.cameraMatrix.mat4);
});
- test('Behavior of slerp() for camera moved by pan()', function() {
+ test('Behavior of slerp() for camera moved by pan()', function () {
myCam = myp5.createCamera();
const cam0 = myCam.copy();
const cam1 = myCam.copy();
@@ -885,14 +822,14 @@ suite('p5.Camera', function() {
cam4.pan(Math.PI * 1.1);
// Compare these views with the view set by slerp().
- myCam.slerp(cam0, cam1, 4/9);
+ myCam.slerp(cam0, cam1, 4 / 9);
expectCameraMatricesAreClose(myCam, cam2);
- myCam.slerp(cam0, cam1, -2/9);
+ myCam.slerp(cam0, cam1, -2 / 9);
expectCameraMatricesAreClose(myCam, cam3);
- myCam.slerp(cam0, cam1, 11/9);
+ myCam.slerp(cam0, cam1, 11 / 9);
expectCameraMatricesAreClose(myCam, cam4);
});
- test('Behavior of slerp() for camera moved by tilt()', function() {
+ test('Behavior of slerp() for camera moved by tilt()', function () {
myCam = myp5.createCamera();
const cam0 = myCam.copy();
const cam1 = myCam.copy();
@@ -907,14 +844,14 @@ suite('p5.Camera', function() {
cam4.tilt(Math.PI * 0.4);
// Compare these views with the view set by slerp().
- myCam.slerp(cam0, cam1, 1/3);
+ myCam.slerp(cam0, cam1, 1 / 3);
expectCameraMatricesAreClose(myCam, cam2);
- myCam.slerp(cam0, cam1, -2/3);
+ myCam.slerp(cam0, cam1, -2 / 3);
expectCameraMatricesAreClose(myCam, cam3);
- myCam.slerp(cam0, cam1, 4/3);
+ myCam.slerp(cam0, cam1, 4 / 3);
expectCameraMatricesAreClose(myCam, cam4);
});
- test('Behavior of slerp() for camera moved by _orbit()', function() {
+ test('Behavior of slerp() for camera moved by _orbit()', function () {
myCam = myp5.createCamera();
const cam0 = myCam.copy();
const cam1 = myCam.copy();
@@ -939,21 +876,21 @@ suite('p5.Camera', function() {
cam8._orbit(0, Math.PI * 1.1, 0.11);
// Compare these views with the view set by slerp().
- myCam.slerp(cam0, cam1, 3/8);
+ myCam.slerp(cam0, cam1, 3 / 8);
expectCameraMatricesAreClose(myCam, cam3);
- myCam.slerp(cam0, cam1, -7/8);
+ myCam.slerp(cam0, cam1, -7 / 8);
expectCameraMatricesAreClose(myCam, cam4);
- myCam.slerp(cam0, cam1, 11/8);
+ myCam.slerp(cam0, cam1, 11 / 8);
expectCameraMatricesAreClose(myCam, cam5);
- myCam.slerp(cam0, cam2, 3/7);
+ myCam.slerp(cam0, cam2, 3 / 7);
expectCameraMatricesAreClose(myCam, cam6);
- myCam.slerp(cam0, cam2, -4/7);
+ myCam.slerp(cam0, cam2, -4 / 7);
expectCameraMatricesAreClose(myCam, cam7);
- myCam.slerp(cam0, cam2, 11/7);
+ myCam.slerp(cam0, cam2, 11 / 7);
expectCameraMatricesAreClose(myCam, cam8);
});
- test('Behavior of slerp() for camera moved by _orbitFree()', function() {
+ test('Behavior of slerp() for camera moved by _orbitFree()', function () {
myCam = myp5.createCamera();
const cam0 = myCam.copy();
const cam1 = myCam.copy();
@@ -968,14 +905,14 @@ suite('p5.Camera', function() {
cam4._orbitFree(Math.PI * 1.2, Math.PI * 0.9, 0.12);
// Compare these views with the view set by slerp().
- myCam.slerp(cam0, cam1, 1/2);
+ myCam.slerp(cam0, cam1, 1 / 2);
expectCameraMatricesAreClose(myCam, cam2);
- myCam.slerp(cam0, cam1, -1/4);
+ myCam.slerp(cam0, cam1, -1 / 4);
expectCameraMatricesAreClose(myCam, cam3);
- myCam.slerp(cam0, cam1, 3/2);
+ myCam.slerp(cam0, cam1, 3 / 2);
expectCameraMatricesAreClose(myCam, cam4);
});
- test('if viewpoints of cam0, cam1 are same, all the same.', function() {
+ test('if viewpoints of cam0, cam1 are same, all the same.', function () {
myCam = myp5.createCamera();
const cam0 = myCam.copy();
cam0.camera(22, 37, 49, 0, 1, 2, 3, 4, -1);
@@ -990,7 +927,7 @@ suite('p5.Camera', function() {
expect(myCam.eyeZ).to.be.closeTo(cam0.eyeZ, 0.00001);
}
});
- test('if center of cam0, cam1 are same, all the same.', function() {
+ test('if center of cam0, cam1 are same, all the same.', function () {
myCam = myp5.createCamera();
const cam0 = myCam.copy();
cam0.camera(0, 1, 2, 22, 37, 49, 3, 4, -1);
@@ -1005,7 +942,7 @@ suite('p5.Camera', function() {
expect(myCam.centerZ).to.be.closeTo(cam0.centerZ, 0.00001);
}
});
- test('if all camera is ortho, 0,5 component is interpolated', function() {
+ test('if all camera is ortho, 0,5 component is interpolated', function () {
myCam = myp5.createCamera();
myCam.ortho(-50, 50, -50, 50, 0, 1000);
const cam0 = myCam.copy();
@@ -1020,14 +957,18 @@ suite('p5.Camera', function() {
// to make sure it is logarithmically interpolated.
const p0_0 = cam0.projMatrix.mat4[0];
const p1_0 = cam1.projMatrix.mat4[0];
- expect(myCam.projMatrix.mat4[0])
- .to.be.closeTo(p0_0 * Math.pow(p1_0 / p0_0, 0.3), 0.00001);
+ expect(myCam.projMatrix.mat4[0]).to.be.closeTo(
+ p0_0 * Math.pow(p1_0 / p0_0, 0.3),
+ 0.00001
+ );
const p0_5 = cam0.projMatrix.mat4[5];
const p1_5 = cam1.projMatrix.mat4[5];
- expect(myCam.projMatrix.mat4[5])
- .to.be.closeTo(p0_5 * Math.pow(p1_5 / p0_5, 0.3), 0.00001);
+ expect(myCam.projMatrix.mat4[5]).to.be.closeTo(
+ p0_5 * Math.pow(p1_5 / p0_5, 0.3),
+ 0.00001
+ );
});
- test('Preventing bugs from occurring by changes in slerp spec', function() {
+ test('Preventing bugs from occurring by changes in slerp spec', function () {
myCam = myp5.createCamera();
const cam0 = myp5.createCamera();
const cam1 = myp5.createCamera();
@@ -1044,8 +985,8 @@ suite('p5.Camera', function() {
});
});
- suite('Helper Functions', function() {
- test('copy() returns a new p5.Camera object', function() {
+ suite('Helper Functions', function () {
+ test('copy() returns a new p5.Camera object', function () {
var newCam = myCam.copy();
assert.instanceOf(newCam, p5.Camera);
@@ -1066,7 +1007,7 @@ suite('p5.Camera', function() {
assert.deepCloseTo(newCam.projMatrix.mat4, myCam.projMatrix.mat4);
});
- test('_getLocalAxes() returns three normalized, orthogonal vectors', function() {
+ test('_getLocalAxes() returns three normalized, orthogonal vectors', function () {
var local = myCam._getLocalAxes();
// assert that all returned values are numbers
@@ -1108,15 +1049,15 @@ suite('p5.Camera', function() {
});
});
- suite('RendererGL Current Camera', function() {
+ suite('RendererGL Current Camera', function () {
test('_isActive() returns true for a camera created with createCamera(),\
- and false for another p5.Camera', function() {
+ and false for another p5.Camera', function () {
var myCam2 = myp5.createCamera();
myp5.setCamera(myCam2);
assert.isTrue(myCam2._isActive());
assert.isFalse(myCam._isActive());
});
- test('setCamera() correctly sets RendererGL current camera', function() {
+ test('setCamera() correctly sets RendererGL current camera', function () {
var myCam2 = myp5.createCamera();
var myCam3 = myp5.createCamera();
myp5.setCamera(myCam2);
@@ -1126,7 +1067,7 @@ suite('p5.Camera', function() {
myp5.setCamera(myCam);
assert.deepCloseTo(myCam, myp5._renderer.states.curCamera);
});
- test("Camera's Renderer is correctly set after setAttributes", function() {
+ test("Camera's Renderer is correctly set after setAttributes", function () {
var myCam2 = myp5.createCamera();
assert.deepCloseTo(myCam2, myp5._renderer.states.curCamera);
myp5.setAttributes('antialias', true);
@@ -1134,8 +1075,8 @@ suite('p5.Camera', function() {
});
});
- suite('Camera attributes after resizing', function() {
- test('Camera position is the same', function() {
+ suite('Camera attributes after resizing', function () {
+ test('Camera position is the same', function () {
myp5.createCanvas(1, 1, myp5.WEBGL);
myp5.noStroke();
myp5.pixelDensity(1);
@@ -1166,7 +1107,7 @@ suite('p5.Camera', function() {
assert.equal(cam.eyeX, 10);
});
- test('Camera rotation is the same', function() {
+ test('Camera rotation is the same', function () {
myp5.createCanvas(1, 1, myp5.WEBGL);
myp5.noStroke();
myp5.pixelDensity(1);
diff --git a/test/unit/webgl/p5.Framebuffer.js b/test/unit/webgl/p5.Framebuffer.js
index 4ff9b128cb..4147edb174 100644
--- a/test/unit/webgl/p5.Framebuffer.js
+++ b/test/unit/webgl/p5.Framebuffer.js
@@ -8,9 +8,9 @@ const mockAnchorElement = vi.mockObject({
});
const originalCreateElement = document.createElement;
vi.spyOn(document, 'createElement').mockImplementation((...args) => {
- if(args[0] !== 'a'){
+ if (args[0] !== 'a') {
return originalCreateElement.apply(document, args);
- }else{
+ } else {
return mockAnchorElement;
}
});
@@ -31,29 +31,29 @@ expect.extend({
}
});
-suite('p5.Framebuffer', function() {
+suite('p5.Framebuffer', function () {
let myp5;
let prevPixelRatio;
- beforeAll(function() {
+ beforeAll(function () {
prevPixelRatio = window.devicePixelRatio;
window.devicePixelRatio = 1;
- myp5 = new p5(function(p) {
- p.setup = function() {};
- p.draw = function() {};
+ myp5 = new p5(function (p) {
+ p.setup = function () {};
+ p.draw = function () {};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
window.devicePixelRatio = prevPixelRatio;
});
- afterEach(function(){
+ afterEach(function () {
vi.clearAllMocks();
});
- suite('formats and channels', function() {
+ suite('formats and channels', function () {
function testWithConfiguration(
version,
format,
@@ -61,44 +61,35 @@ suite('p5.Framebuffer', function() {
antialias,
depth
) {
- test(
- `framebuffers work with WebGL ${version}, ${format} ${channels} ${depth || 'no'} depth ${antialias ? ' antialiased' : ''}`,
- function() {
- myp5.createCanvas(10, 10, myp5.WEBGL);
- myp5.setAttributes({ version });
-
- // Draw a box to the framebuffer
- const fbo = myp5.createFramebuffer({
- format,
- channels,
- antialias,
- depth: depth !== null,
- depthFormat: depth
- });
- fbo.draw(() => {
- myp5.background(255);
- myp5.noStroke();
- myp5.fill('red');
- myp5.box(5, 5, 5);
- });
-
- // Draw the framebuffer to the canvas
- myp5.background(0);
+ test(`framebuffers work with WebGL ${version}, ${format} ${channels} ${depth || 'no'} depth ${antialias ? ' antialiased' : ''}`, function () {
+ myp5.createCanvas(10, 10, myp5.WEBGL);
+ myp5.setAttributes({ version });
+
+ // Draw a box to the framebuffer
+ const fbo = myp5.createFramebuffer({
+ format,
+ channels,
+ antialias,
+ depth: depth !== null,
+ depthFormat: depth
+ });
+ fbo.draw(() => {
+ myp5.background(255);
myp5.noStroke();
- myp5.texture(fbo);
- myp5.plane(myp5.width, -myp5.height);
+ myp5.fill('red');
+ myp5.box(5, 5, 5);
+ });
- // Make sure it drew
- assert.deepEqual(
- myp5.get(0, 0),
- [255, 255, 255, 255]
- );
- assert.deepEqual(
- myp5.get(5, 5),
- [255, 0, 0, 255]
- );
- }
- );
+ // Draw the framebuffer to the canvas
+ myp5.background(0);
+ myp5.noStroke();
+ myp5.texture(fbo);
+ myp5.plane(myp5.width, -myp5.height);
+
+ // Make sure it drew
+ assert.deepEqual(myp5.get(0, 0), [255, 255, 255, 255]);
+ assert.deepEqual(myp5.get(5, 5), [255, 0, 0, 255]);
+ });
}
const versions = [1, 2];
@@ -125,7 +116,7 @@ suite('p5.Framebuffer', function() {
}
});
- suite('sizing', function() {
+ suite('sizing', function () {
let glStub;
afterEach(() => {
@@ -134,7 +125,7 @@ suite('p5.Framebuffer', function() {
}
});
- test('auto-sized framebuffers change size with their canvas', function() {
+ test('auto-sized framebuffers change size with their canvas', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.pixelDensity(1);
const fbo = myp5.createFramebuffer();
@@ -154,7 +145,7 @@ suite('p5.Framebuffer', function() {
expect(fbo.color.rawTexture()).not.to.equal(oldTexture);
});
- test('manually-sized framebuffers do not change size with their canvas', function() {
+ test('manually-sized framebuffers do not change size with their canvas', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.pixelDensity(3);
const fbo = myp5.createFramebuffer({ width: 20, height: 20, density: 1 });
@@ -174,7 +165,7 @@ suite('p5.Framebuffer', function() {
expect(fbo.color.rawTexture().texture).to.equal(oldTexture.texture);
});
- test('manually-sized framebuffers can be made auto-sized', function() {
+ test('manually-sized framebuffers can be made auto-sized', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.pixelDensity(3);
const fbo = myp5.createFramebuffer({ width: 20, height: 20, density: 1 });
@@ -198,10 +189,10 @@ suite('p5.Framebuffer', function() {
expect(fbo.color.rawTexture()).not.to.equal(oldTexture);
});
- suite('resizing', function() {
+ suite('resizing', function () {
let fbo;
let oldTexture;
- beforeEach(function() {
+ beforeEach(function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.pixelDensity(1);
fbo = myp5.createFramebuffer();
@@ -211,7 +202,7 @@ suite('p5.Framebuffer', function() {
fbo.pixelDensity(2);
});
- test('framebuffers can be resized', function() {
+ test('framebuffers can be resized', function () {
expect(fbo.width).to.equal(5);
expect(fbo.height).to.equal(15);
expect(fbo.density).to.equal(2);
@@ -220,7 +211,7 @@ suite('p5.Framebuffer', function() {
expect(fbo.color.rawTexture()).not.to.equal(oldTexture);
});
- test('resizing a framebuffer turns off auto-sizing', function() {
+ test('resizing a framebuffer turns off auto-sizing', function () {
oldTexture = fbo.color.rawTexture();
myp5.resizeCanvas(20, 20);
@@ -234,7 +225,7 @@ suite('p5.Framebuffer', function() {
expect(fbo.color.rawTexture().texture).to.equal(oldTexture.texture);
});
- test('resizes the framebuffer by createFramebuffer based on max texture size', function() {
+ test('resizes the framebuffer by createFramebuffer based on max texture size', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
delete myp5._renderer._maxTextureSize;
glStub = vi.spyOn(myp5._renderer, '_getMaxTextureSize');
@@ -246,7 +237,7 @@ suite('p5.Framebuffer', function() {
expect(fbo.height).to.equal(100);
});
- test('resizes the framebuffer by resize method based on max texture size', function() {
+ test('resizes the framebuffer by resize method based on max texture size', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
delete myp5._renderer._maxTextureSize;
glStub = vi.spyOn(myp5._renderer, '_getMaxTextureSize');
@@ -261,7 +252,7 @@ suite('p5.Framebuffer', function() {
});
});
- suite('rendering', function() {
+ suite('rendering', function () {
function setupAndReturnFramebuffer() {
myp5.createCanvas(10, 10, myp5.WEBGL);
@@ -277,7 +268,7 @@ suite('p5.Framebuffer', function() {
return fbo;
}
- test('rendering works with fbo.color as a texture', function() {
+ test('rendering works with fbo.color as a texture', function () {
const fbo = setupAndReturnFramebuffer();
// Draw the framebuffer to the canvas
@@ -286,13 +277,10 @@ suite('p5.Framebuffer', function() {
myp5.texture(fbo.color);
myp5.plane(myp5.width, -myp5.height);
- assert.deepEqual(
- myp5.get(5, 5),
- [255, 0, 0, 255]
- );
+ assert.deepEqual(myp5.get(5, 5), [255, 0, 0, 255]);
});
- test('rendering works with fbo as a texture', function() {
+ test('rendering works with fbo as a texture', function () {
const fbo = setupAndReturnFramebuffer();
// Draw the framebuffer to the canvas
@@ -301,13 +289,10 @@ suite('p5.Framebuffer', function() {
myp5.texture(fbo);
myp5.plane(myp5.width, -myp5.height);
- assert.deepEqual(
- myp5.get(5, 5),
- [255, 0, 0, 255]
- );
+ assert.deepEqual(myp5.get(5, 5), [255, 0, 0, 255]);
});
- test('rendering works with fbo.depth as a texture', function() {
+ test('rendering works with fbo.depth as a texture', function () {
const fbo = setupAndReturnFramebuffer();
// Draw the framebuffer to the canvas
@@ -321,7 +306,7 @@ suite('p5.Framebuffer', function() {
});
});
- test('Framebuffers work on p5.Graphics', function() {
+ test('Framebuffers work on p5.Graphics', function () {
const graphic = myp5.createGraphics(10, 10, myp5.WEBGL);
// Draw a box to the framebuffer
@@ -340,18 +325,12 @@ suite('p5.Framebuffer', function() {
graphic.plane(graphic.width, -graphic.height);
// Make sure it drew
- assert.deepEqual(
- graphic.get(0, 0),
- [255, 255, 255, 255]
- );
- assert.deepEqual(
- graphic.get(5, 5),
- [255, 0, 0, 255]
- );
+ assert.deepEqual(graphic.get(0, 0), [255, 255, 255, 255]);
+ assert.deepEqual(graphic.get(5, 5), [255, 0, 0, 255]);
});
- suite('remove()', function() {
- test('remove() cleans up textures', function() {
+ suite('remove()', function () {
+ test('remove() cleans up textures', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
const fbo = myp5.createFramebuffer();
const numTextures = myp5._renderer.textures.size;
@@ -359,28 +338,25 @@ suite('p5.Framebuffer', function() {
expect(myp5._renderer.textures.size).to.equal(numTextures - 2);
});
- test(
- 'remove() cleans up textures when the framebuffer has no depth',
- function() {
- myp5.createCanvas(10, 10, myp5.WEBGL);
- const fbo = myp5.createFramebuffer({ depth: false });
- const numTextures = myp5._renderer.textures.size;
- fbo.remove();
- expect(myp5._renderer.textures.size).to.equal(numTextures - 1);
- }
- );
+ test('remove() cleans up textures when the framebuffer has no depth', function () {
+ myp5.createCanvas(10, 10, myp5.WEBGL);
+ const fbo = myp5.createFramebuffer({ depth: false });
+ const numTextures = myp5._renderer.textures.size;
+ fbo.remove();
+ expect(myp5._renderer.textures.size).to.equal(numTextures - 1);
+ });
});
- suite('defaultCamera', function() {
+ suite('defaultCamera', function () {
let fbo;
- beforeEach(function() {
+ beforeEach(function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.pixelDensity(1);
fbo = myp5.createFramebuffer({ width: 5, height: 15 });
});
- suite('the default camera', function() {
- test('it uses the aspect ratio of the framebuffer', function() {
+ suite('the default camera', function () {
+ test('it uses the aspect ratio of the framebuffer', function () {
expect(fbo.defaultCamera.aspectRatio).to.equal(5 / 15);
expect(fbo.defaultCamera.cameraFOV).to.be.closeTo(
2.0 * Math.atan(15 / 2 / 800),
@@ -388,18 +364,32 @@ suite('p5.Framebuffer', function() {
);
const z = -800;
const expectedCameraMatrix = [
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, z, 1
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ z,
+ 1
];
for (let i = 0; i < expectedCameraMatrix.length; i++) {
- expect(fbo.defaultCamera.cameraMatrix.mat4[i])
- .to.be.closeTo(expectedCameraMatrix[i], 0.01);
+ expect(fbo.defaultCamera.cameraMatrix.mat4[i]).to.be.closeTo(
+ expectedCameraMatrix[i],
+ 0.01
+ );
}
});
- test('it updates the aspect ratio after resizing', function() {
+ test('it updates the aspect ratio after resizing', function () {
fbo.resize(20, 10);
expect(fbo.defaultCamera.aspectRatio).to.equal(2);
expect(fbo.defaultCamera.cameraFOV).to.be.closeTo(
@@ -408,34 +398,48 @@ suite('p5.Framebuffer', function() {
);
const z = -800;
const expectedCameraMatrix = [
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, z, 1
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ z,
+ 1
];
for (let i = 0; i < expectedCameraMatrix.length; i++) {
- expect(fbo.defaultCamera.cameraMatrix.mat4[i])
- .to.be.closeTo(expectedCameraMatrix[i], 0.01);
+ expect(fbo.defaultCamera.cameraMatrix.mat4[i]).to.be.closeTo(
+ expectedCameraMatrix[i],
+ 0.01
+ );
}
});
});
});
- suite('get()', function() {
- test('get(x, y) loads a pixel', function() {
+ suite('get()', function () {
+ test('get(x, y) loads a pixel', function () {
myp5.createCanvas(20, 20, myp5.WEBGL);
const fbo = myp5.createFramebuffer();
fbo.draw(() => {
myp5.noStroke();
myp5.push();
- myp5.translate(-myp5.width/2, -myp5.height/2);
+ myp5.translate(-myp5.width / 2, -myp5.height / 2);
myp5.fill('red');
myp5.sphere(5);
myp5.pop();
myp5.push();
- myp5.translate(myp5.width/2, myp5.height/2);
+ myp5.translate(myp5.width / 2, myp5.height / 2);
myp5.fill('blue');
myp5.sphere(5);
myp5.pop();
@@ -446,20 +450,20 @@ suite('p5.Framebuffer', function() {
assert.deepEqual(fbo.get(19, 19), [0, 0, 255, 255]);
});
- test('get() creates a p5.Image with equivalent pixels', function() {
+ test('get() creates a p5.Image with equivalent pixels', function () {
myp5.createCanvas(20, 20, myp5.WEBGL);
const fbo = myp5.createFramebuffer();
fbo.draw(() => {
myp5.noStroke();
myp5.push();
- myp5.translate(-myp5.width/2, -myp5.height/2);
+ myp5.translate(-myp5.width / 2, -myp5.height / 2);
myp5.fill('red');
myp5.sphere(5);
myp5.pop();
myp5.push();
- myp5.translate(myp5.width/2, myp5.height/2);
+ myp5.translate(myp5.width / 2, myp5.height / 2);
myp5.fill('blue');
myp5.sphere(5);
myp5.pop();
@@ -476,7 +480,7 @@ suite('p5.Framebuffer', function() {
}
});
- test('get() creates a p5.Image matching the source pixel density', function() {
+ test('get() creates a p5.Image matching the source pixel density', function () {
const mainCanvas = myp5.createCanvas(20, 20, myp5.WEBGL);
myp5.pixelDensity(2);
const fbo = myp5.createFramebuffer();
@@ -485,13 +489,13 @@ suite('p5.Framebuffer', function() {
myp5.background(255);
myp5.push();
- myp5.translate(-myp5.width/2, -myp5.height/2);
+ myp5.translate(-myp5.width / 2, -myp5.height / 2);
myp5.fill('red');
myp5.sphere(5);
myp5.pop();
myp5.push();
- myp5.translate(myp5.width/2, myp5.height/2);
+ myp5.translate(myp5.width / 2, myp5.height / 2);
myp5.fill('blue');
myp5.sphere(5);
myp5.pop();
@@ -512,45 +516,42 @@ suite('p5.Framebuffer', function() {
});
});
- test(
- 'loadPixels works in arbitrary order for multiple framebuffers',
- function() {
- myp5.createCanvas(20, 20, myp5.WEBGL);
- const fbo1 = myp5.createFramebuffer();
- const fbo2 = myp5.createFramebuffer();
-
- fbo1.loadPixels();
- fbo2.loadPixels();
- for (let i = 0; i < fbo1.pixels.length; i += 4) {
- // Set everything red
- fbo1.pixels[i] = 255;
- fbo1.pixels[i + 1] = 0;
- fbo1.pixels[i + 2] = 0;
- fbo1.pixels[i + 3] = 255;
- }
- for (let i = 0; i < fbo2.pixels.length; i += 4) {
- // Set everything blue
- fbo2.pixels[i] = 0;
- fbo2.pixels[i + 1] = 0;
- fbo2.pixels[i + 2] = 255;
- fbo2.pixels[i + 3] = 255;
- }
- fbo2.updatePixels();
- fbo1.updatePixels();
+ test('loadPixels works in arbitrary order for multiple framebuffers', function () {
+ myp5.createCanvas(20, 20, myp5.WEBGL);
+ const fbo1 = myp5.createFramebuffer();
+ const fbo2 = myp5.createFramebuffer();
+
+ fbo1.loadPixels();
+ fbo2.loadPixels();
+ for (let i = 0; i < fbo1.pixels.length; i += 4) {
+ // Set everything red
+ fbo1.pixels[i] = 255;
+ fbo1.pixels[i + 1] = 0;
+ fbo1.pixels[i + 2] = 0;
+ fbo1.pixels[i + 3] = 255;
+ }
+ for (let i = 0; i < fbo2.pixels.length; i += 4) {
+ // Set everything blue
+ fbo2.pixels[i] = 0;
+ fbo2.pixels[i + 1] = 0;
+ fbo2.pixels[i + 2] = 255;
+ fbo2.pixels[i + 3] = 255;
+ }
+ fbo2.updatePixels();
+ fbo1.updatePixels();
- myp5.imageMode(myp5.CENTER);
+ myp5.imageMode(myp5.CENTER);
- myp5.clear();
- myp5.image(fbo1, 0, 0);
- assert.deepEqual(myp5.get(0, 0), [255, 0, 0, 255]);
+ myp5.clear();
+ myp5.image(fbo1, 0, 0);
+ assert.deepEqual(myp5.get(0, 0), [255, 0, 0, 255]);
- myp5.clear();
- myp5.image(fbo2, 0, 0);
- assert.deepEqual(myp5.get(0, 0), [0, 0, 255, 255]);
- }
- );
+ myp5.clear();
+ myp5.image(fbo2, 0, 0);
+ assert.deepEqual(myp5.get(0, 0), [0, 0, 255, 255]);
+ });
- test('Strokes work on and off of framebuffers', function() {
+ test('Strokes work on and off of framebuffers', function () {
myp5.createCanvas(20, 20, myp5.WEBGL);
const fbo = myp5.createFramebuffer();
@@ -561,7 +562,7 @@ suite('p5.Framebuffer', function() {
myp5.strokeWeight(20);
myp5.beginShape();
for (let i = 0; i < 20; i++) {
- const angle = i/20*myp5.TWO_PI;
+ const angle = (i / 20) * myp5.TWO_PI;
myp5.vertex(
100 * myp5.cos(angle),
// Add an offset to make sure results don't get flipped
@@ -582,19 +583,19 @@ suite('p5.Framebuffer', function() {
assert.deepEqual(fboPixels, mainPixels);
});
- suite('nesting', function() {
+ suite('nesting', function () {
for (const antialias of [false, true]) {
- suite(`with antialiasing ${antialias ? 'on' : 'off'}`, function() {
+ suite(`with antialiasing ${antialias ? 'on' : 'off'}`, function () {
let fbo1;
let fbo2;
- beforeEach(function() {
+ beforeEach(function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.pixelDensity(1);
fbo1 = myp5.createFramebuffer({ antialias });
fbo2 = myp5.createFramebuffer({ antialias });
});
- test('one can nest active framebuffers', function() {
+ test('one can nest active framebuffers', function () {
fbo1.begin();
myp5.fill('red');
myp5.circle(0, 0, 10);
@@ -610,8 +611,8 @@ suite('p5.Framebuffer', function() {
assert.deepEqual(fbo2.get(5, 5), [0, 0, 255, 255]);
});
- test('end() in the wrong order throws an error', function() {
- expect(function() {
+ test('end() in the wrong order throws an error', function () {
+ expect(function () {
fbo1.begin();
fbo2.begin();
fbo1.end();
@@ -619,7 +620,7 @@ suite('p5.Framebuffer', function() {
}).to.throw(/another Framebuffer is active/);
});
- test('one can read a nested framebuffer', function() {
+ test('one can read a nested framebuffer', function () {
myp5.imageMode(myp5.CENTER);
fbo1.begin();
@@ -639,37 +640,33 @@ suite('p5.Framebuffer', function() {
}
});
- suite('texture filtering', function() {
- test('can create a framebuffer that uses NEAREST texture filtering',
- function() {
- myp5.createCanvas(10, 10, myp5.WEBGL);
- const fbo = myp5.createFramebuffer({
- textureFiltering: myp5.NEAREST
- });
-
- assert.equal(
- fbo.color.framebuffer.colorP5Texture.minFilter, myp5.NEAREST
- );
- assert.equal(
- fbo.color.framebuffer.colorP5Texture.magFilter, myp5.NEAREST
- );
+ suite('texture filtering', function () {
+ test('can create a framebuffer that uses NEAREST texture filtering', function () {
+ myp5.createCanvas(10, 10, myp5.WEBGL);
+ const fbo = myp5.createFramebuffer({
+ textureFiltering: myp5.NEAREST
});
- test('can create a framebuffer that uses LINEAR texture filtering',
- function() {
- myp5.createCanvas(10, 10, myp5.WEBGL);
- // LINEAR should be the default
- const fbo = myp5.createFramebuffer({});
- assert.equal(
- fbo.color.framebuffer.colorP5Texture.minFilter, myp5.LINEAR
- );
- assert.equal(
- fbo.color.framebuffer.colorP5Texture.magFilter, myp5.LINEAR
- );
- });
+ assert.equal(
+ fbo.color.framebuffer.colorP5Texture.minFilter,
+ myp5.NEAREST
+ );
+ assert.equal(
+ fbo.color.framebuffer.colorP5Texture.magFilter,
+ myp5.NEAREST
+ );
+ });
+ test('can create a framebuffer that uses LINEAR texture filtering', function () {
+ myp5.createCanvas(10, 10, myp5.WEBGL);
+ // LINEAR should be the default
+ const fbo = myp5.createFramebuffer({});
+
+ assert.equal(fbo.color.framebuffer.colorP5Texture.minFilter, myp5.LINEAR);
+ assert.equal(fbo.color.framebuffer.colorP5Texture.magFilter, myp5.LINEAR);
+ });
});
- suite('saveCanvas', function() {
+ suite('saveCanvas', function () {
test('should download a png file', async () => {
myp5.createCanvas(100, 100, myp5.WEBGL);
const fbo = myp5.createFramebuffer();
diff --git a/test/unit/webgl/p5.Geometry.js b/test/unit/webgl/p5.Geometry.js
index f48ecbef2e..44b492b859 100644
--- a/test/unit/webgl/p5.Geometry.js
+++ b/test/unit/webgl/p5.Geometry.js
@@ -1,35 +1,35 @@
import p5 from '../../../src/app.js';
import { vi } from 'vitest';
-suite('p5.Geometry', function() {
+suite('p5.Geometry', function () {
let myp5;
- beforeAll(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {};
- p.draw = function() {};
+ beforeAll(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {};
+ p.draw = function () {};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('generating edge geometry', function() {
+ suite('generating edge geometry', function () {
let geom;
- beforeEach(function() {
+ beforeEach(function () {
geom = new p5.Geometry(undefined, undefined, undefined, myp5._renderer);
vi.spyOn(geom, '_addCap');
vi.spyOn(geom, '_addJoin');
vi.spyOn(geom, '_addSegment');
});
- afterEach(function() {
+ afterEach(function () {
vi.restoreAllMocks();
});
- test('single polyline', function() {
+ test('single polyline', function () {
geom.vertices.push(
myp5.createVector(0, 0),
myp5.createVector(0, 100),
@@ -44,7 +44,7 @@ suite('p5.Geometry', function() {
expect(geom._addJoin).toHaveBeenCalledTimes(2);
});
- test('straight line', function() {
+ test('straight line', function () {
geom.vertices.push(
myp5.createVector(0, 0),
myp5.createVector(0, 100),
@@ -59,7 +59,7 @@ suite('p5.Geometry', function() {
expect(geom._addJoin).toHaveBeenCalledTimes(0);
});
- test('two disconnected polylines', function() {
+ test('two disconnected polylines', function () {
geom.vertices.push(
myp5.createVector(0, 0),
myp5.createVector(0, 100),
@@ -74,7 +74,7 @@ suite('p5.Geometry', function() {
expect(geom._addJoin).toHaveBeenCalledTimes(0);
});
- test('polyline that loops back', function() {
+ test('polyline that loops back', function () {
geom.vertices.push(
myp5.createVector(0, 0),
myp5.createVector(0, 100),
@@ -89,7 +89,7 @@ suite('p5.Geometry', function() {
expect(geom._addJoin).toHaveBeenCalledTimes(4);
});
- test('calculateBoundingBox()', function() {
+ test('calculateBoundingBox()', function () {
geom.vertices.push(
myp5.createVector(0, 0, 0),
myp5.createVector(10, 20, 30),
@@ -102,8 +102,7 @@ suite('p5.Geometry', function() {
assert.deepEqual(boundingBox.offset.array(), [2.5, 10, 15]);
});
-
- test('degenerate edge in the middle', function() {
+ test('degenerate edge in the middle', function () {
geom.vertices.push(
myp5.createVector(0, 0),
myp5.createVector(0, 100),
@@ -121,7 +120,7 @@ suite('p5.Geometry', function() {
expect(geom._addJoin).toHaveBeenCalledTimes(2);
});
- test('degenerate edge at the end', function() {
+ test('degenerate edge at the end', function () {
geom.vertices.push(
myp5.createVector(0, 0),
myp5.createVector(0, 100),
@@ -139,7 +138,7 @@ suite('p5.Geometry', function() {
expect(geom._addJoin).toHaveBeenCalledTimes(2);
});
- test('degenerate edge between two disconnected polylines', function() {
+ test('degenerate edge between two disconnected polylines', function () {
geom.vertices.push(
myp5.createVector(0, 0),
myp5.createVector(0, 100),
@@ -156,7 +155,7 @@ suite('p5.Geometry', function() {
});
});
- suite('buildGeometry', function() {
+ suite('buildGeometry', function () {
const checkLights = () => myp5.lights();
const checkMaterials = () => {
myp5.fill('#ffea30');
@@ -198,77 +197,86 @@ suite('p5.Geometry', function() {
}
}
- test('Transforms are applied to models', function() {
- assertGeometryRendersMatch(function() {
- myp5.push();
- myp5.translate(0, -20);
- for (let i = 0; i < 4; i++) {
- myp5.box(8);
- myp5.translate(0, 40/3);
- myp5.rotateY(myp5.PI * 0.2);
- }
- myp5.pop();
- }, [checkMaterials]);
+ test('Transforms are applied to models', function () {
+ assertGeometryRendersMatch(
+ function () {
+ myp5.push();
+ myp5.translate(0, -20);
+ for (let i = 0; i < 4; i++) {
+ myp5.box(8);
+ myp5.translate(0, 40 / 3);
+ myp5.rotateY(myp5.PI * 0.2);
+ }
+ myp5.pop();
+ },
+ [checkMaterials]
+ );
});
- test('Immediate mode constructs are translated correctly', function() {
- assertGeometryRendersMatch(function() {
- myp5.scale(1/6);
- myp5.push();
- myp5.translate(100, -50);
- myp5.scale(0.5);
- myp5.rotateX(myp5.PI/4);
- myp5.cone();
- myp5.pop();
- myp5.cone();
-
- myp5.beginShape();
- myp5.bezierOrder(2);
- myp5.bezierVertex(-20, -50);
-
- myp5.bezierVertex(-40, -70);
- myp5.bezierVertex(0, -60);
- myp5.endShape();
-
- myp5.beginShape(myp5.TRIANGLE_STRIP);
- for (let y = 20; y <= 60; y += 10) {
- for (let x of [20, 60]) {
- myp5.vertex(x, y);
+ test('Immediate mode constructs are translated correctly', function () {
+ assertGeometryRendersMatch(
+ function () {
+ myp5.scale(1 / 6);
+ myp5.push();
+ myp5.translate(100, -50);
+ myp5.scale(0.5);
+ myp5.rotateX(myp5.PI / 4);
+ myp5.cone();
+ myp5.pop();
+ myp5.cone();
+
+ myp5.beginShape();
+ myp5.bezierOrder(2);
+ myp5.bezierVertex(-20, -50);
+
+ myp5.bezierVertex(-40, -70);
+ myp5.bezierVertex(0, -60);
+ myp5.endShape();
+
+ myp5.beginShape(myp5.TRIANGLE_STRIP);
+ for (let y = 20; y <= 60; y += 10) {
+ for (let x of [20, 60]) {
+ myp5.vertex(x, y);
+ }
}
- }
- myp5.endShape();
-
- myp5.beginShape();
- myp5.vertex(-100, -120);
- myp5.vertex(-120, -110);
- myp5.vertex(-105, -100);
- myp5.endShape();
- }, [checkLights, checkMaterials, checkNormals]);
+ myp5.endShape();
+
+ myp5.beginShape();
+ myp5.vertex(-100, -120);
+ myp5.vertex(-120, -110);
+ myp5.vertex(-105, -100);
+ myp5.endShape();
+ },
+ [checkLights, checkMaterials, checkNormals]
+ );
});
- test('Vertex colors are captured', function() {
- assertGeometryRendersMatch(function() {
- myp5.push();
- myp5.translate(0, -10);
- myp5.fill('red');
- myp5.sphere(5, 10, 5);
- myp5.pop();
-
- myp5.push();
- myp5.translate(-10, 10);
- myp5.fill('lime');
- myp5.sphere(5, 10, 5);
- myp5.pop();
-
- myp5.push();
- myp5.translate(10, 10);
- myp5.fill('blue');
- myp5.sphere(5, 10, 5);
- myp5.pop();
- }, [checkLights]);
+ test('Vertex colors are captured', function () {
+ assertGeometryRendersMatch(
+ function () {
+ myp5.push();
+ myp5.translate(0, -10);
+ myp5.fill('red');
+ myp5.sphere(5, 10, 5);
+ myp5.pop();
+
+ myp5.push();
+ myp5.translate(-10, 10);
+ myp5.fill('lime');
+ myp5.sphere(5, 10, 5);
+ myp5.pop();
+
+ myp5.push();
+ myp5.translate(10, 10);
+ myp5.fill('blue');
+ myp5.sphere(5, 10, 5);
+ myp5.pop();
+ },
+ [checkLights]
+ );
});
- test('freeGeometry() cleans up resources', function() {
+ test('freeGeometry() cleans up resources', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.pixelDensity(1);
diff --git a/test/unit/webgl/p5.GeometryPart.js b/test/unit/webgl/p5.GeometryPart.js
index ca36364455..00cde74b0b 100644
--- a/test/unit/webgl/p5.GeometryPart.js
+++ b/test/unit/webgl/p5.GeometryPart.js
@@ -1,25 +1,25 @@
import p5 from '../../../src/app.js';
import { vi } from 'vitest';
-suite('p5.GeometryPart', function() {
+suite('p5.GeometryPart', function () {
let myp5;
- beforeAll(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {};
- p.draw = function() {};
+ beforeAll(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {};
+ p.draw = function () {};
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- test('is registered on p5', function() {
+ test('is registered on p5', function () {
expect(p5.GeometryPart).toBeDefined();
});
- test('starts with empty buffers', function() {
+ test('starts with empty buffers', function () {
const part = new p5.GeometryPart('_part_test');
expect(part.vertices).toEqual([]);
expect(part.vertexNormals).toEqual([]);
@@ -28,12 +28,12 @@ suite('p5.GeometryPart', function() {
expect(part.vertexColors).toEqual([]);
});
- test('keeps the gid it was given', function() {
+ test('keeps the gid it was given', function () {
const part = new p5.GeometryPart('parent|part0');
expect(part.gid).toEqual('parent|part0');
});
- test('defaults part state to nulls', function() {
+ test('defaults part state to nulls', function () {
const part = new p5.GeometryPart('_part_test');
expect(part.partState).toEqual({
fill: null,
@@ -44,52 +44,69 @@ suite('p5.GeometryPart', function() {
});
});
- test('uses the part state it was given', function() {
+ test('uses the part state it was given', function () {
const state = { fill: [255, 0, 0], opacity: 0.5 };
const part = new p5.GeometryPart('_part_test', state);
expect(part.partState).toBe(state);
});
- test('each part gets its own buffers', function() {
+ test('each part gets its own buffers', function () {
const a = new p5.GeometryPart('a');
const b = new p5.GeometryPart('b');
a.vertices.push([0, 0, 0]);
expect(b.vertices).toEqual([]);
});
- suite('single-part wrap on p5.Geometry', function() {
- test('a plain geometry gets exactly one part', function() {
- const geom = new p5.Geometry(undefined, undefined, undefined,
- myp5._renderer);
+ suite('single-part wrap on p5.Geometry', function () {
+ test('a plain geometry gets exactly one part', function () {
+ const geom = new p5.Geometry(
+ undefined,
+ undefined,
+ undefined,
+ myp5._renderer
+ );
expect(geom.parts.length).toEqual(1);
});
- test('the part is a live view onto the geometry buffers', function() {
- const geom = new p5.Geometry(undefined, undefined, undefined,
- myp5._renderer);
+ test('the part is a live view onto the geometry buffers', function () {
+ const geom = new p5.Geometry(
+ undefined,
+ undefined,
+ undefined,
+ myp5._renderer
+ );
expect(geom.parts[0].vertices).toBe(geom.vertices);
expect(geom.parts[0].faces).toBe(geom.faces);
});
- test('a single-material geometry is its own part', function() {
- const geom = new p5.Geometry(undefined, undefined, undefined,
- myp5._renderer);
+ test('a single-material geometry is its own part', function () {
+ const geom = new p5.Geometry(
+ undefined,
+ undefined,
+ undefined,
+ myp5._renderer
+ );
geom.gid = 'my-model';
expect(geom.parts[0]).toBe(geom);
expect(geom.parts[0].gid).toEqual('my-model');
});
- test('a built-in primitive also gets one part', function() {
- const geom = new p5.Geometry(1, 1, function() {
- this.vertices.push(new p5.Vector(0, 0, 0));
- }, myp5._renderer);
+ test('a built-in primitive also gets one part', function () {
+ const geom = new p5.Geometry(
+ 1,
+ 1,
+ function () {
+ this.vertices.push(new p5.Vector(0, 0, 0));
+ },
+ myp5._renderer
+ );
expect(geom.parts.length).toEqual(1);
expect(geom.parts[0].vertices.length).toEqual(1);
});
});
- suite('multi-material rendering', function() {
- test('draws a fill per part and a stroke for the model', async function() {
+ suite('multi-material rendering', function () {
+ test('draws a fill per part and a stroke for the model', async function () {
const renderer = myp5.createCanvas(50, 50, myp5.WEBGL);
const model = await myp5.loadModel('/test/unit/assets/octa-color.obj');
diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js
index 90e12e329a..c0a9ed3846 100644
--- a/test/unit/webgl/p5.RendererGL.js
+++ b/test/unit/webgl/p5.RendererGL.js
@@ -3,29 +3,29 @@ import p5 from '../../../src/app.js';
import '../../js/chai_helpers';
const toArray = typedArray => Array.from(typedArray);
-suite('p5.RendererGL', function() {
+suite('p5.RendererGL', function () {
var myp5;
- beforeEach(function() {
- myp5 = new p5(function(p) {
- p.setup = function() {};
- p.draw = function() {};
+ beforeEach(function () {
+ myp5 = new p5(function (p) {
+ p.setup = function () {};
+ p.draw = function () {};
});
});
- afterEach(function() {
+ afterEach(function () {
myp5.remove();
});
- suite('createCanvas(w, h, WEBGL)', function() {
- test('creates a p5.RendererGL renderer', function() {
+ suite('createCanvas(w, h, WEBGL)', function () {
+ test('creates a p5.RendererGL renderer', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
assert.instanceOf(myp5._renderer, p5.RendererGL);
});
});
- suite('noSmooth() canvas position preservation', function() {
- test('should maintain the canvas position after calling noSmooth()', function() {
+ suite('noSmooth() canvas position preservation', function () {
+ test('should maintain the canvas position after calling noSmooth()', function () {
myp5.createCanvas(300, 300, myp5.WEBGL);
let cnv = myp5.canvas;
cnv.style.position = 'absolute';
@@ -41,21 +41,20 @@ suite('p5.RendererGL', function() {
});
});
-
- suite('webglVersion', function() {
- test('should return WEBGL2 by default', function() {
+ suite('webglVersion', function () {
+ test('should return WEBGL2 by default', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
assert.equal(myp5.webglVersion, myp5.WEBGL2);
});
- test('should return WEBGL1 after setAttributes', function() {
+ test('should return WEBGL1 after setAttributes', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.setAttributes({ version: 1 });
assert.equal(myp5.webglVersion, myp5.WEBGL);
});
// NOTE: should graphics always create WebGL2 canvas?
- test.skip('works on p5.Graphics', function() {
+ test.skip('works on p5.Graphics', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.setAttributes({ version: 1 });
const g = myp5.createGraphics(10, 10, myp5.WEBGL);
@@ -63,12 +62,12 @@ suite('p5.RendererGL', function() {
assert.equal(g.webglVersion, myp5.WEBGL2);
});
- suite('when WebGL2 is unavailable', function() {
+ suite('when WebGL2 is unavailable', function () {
let prevGetContext;
- beforeAll(function() {
+ beforeAll(function () {
prevGetContext = HTMLCanvasElement.prototype.getContext;
// Mock WebGL2 being unavailable
- HTMLCanvasElement.prototype.getContext = function(type, attrs) {
+ HTMLCanvasElement.prototype.getContext = function (type, attrs) {
if (type === 'webgl2') {
return undefined;
} else {
@@ -77,31 +76,34 @@ suite('p5.RendererGL', function() {
};
});
- afterAll(function() {
+ afterAll(function () {
// Put back the actual implementation
HTMLCanvasElement.prototype.getContext = prevGetContext;
});
- test('should return WEBGL1', function() {
+ test('should return WEBGL1', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
assert.equal(myp5.webglVersion, myp5.WEBGL);
});
});
});
- suite('p5.strands', function() {
- test('a uniform whose name matches a hook parameter name does not break', function() {
+ suite('p5.strands', function () {
+ test('a uniform whose name matches a hook parameter name does not break', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.pixelDensity(1);
// 'color' is the GLSL parameter name of the getFinalColor hook's first argument.
// Creating a uniform with the same name used to cause a GLSL name clash.
- const myShader = myp5.baseColorShader().modify(() => {
- const color = myp5.uniformFloat('color', 0.5);
- myp5.finalColor.begin();
- myp5.finalColor.set([color, color, color, 1]);
- myp5.finalColor.end();
- }, { myp5 });
+ const myShader = myp5.baseColorShader().modify(
+ () => {
+ const color = myp5.uniformFloat('color', 0.5);
+ myp5.finalColor.begin();
+ myp5.finalColor.set([color, color, color, 1]);
+ myp5.finalColor.end();
+ },
+ { myp5 }
+ );
myp5.background(0);
myp5.noStroke();
@@ -116,8 +118,8 @@ suite('p5.RendererGL', function() {
});
});
- suite('texture binding', function() {
- test('setting a custom texture works', function() {
+ suite('texture binding', function () {
+ test('setting a custom texture works', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.background(255);
@@ -136,7 +138,7 @@ suite('p5.RendererGL', function() {
tex.draw(() => myp5.background('red'));
myp5.shader(myShader);
- myp5.fill('blue')
+ myp5.fill('blue');
myp5.noStroke();
myShader.setUniform('myTex', tex);
@@ -147,7 +149,7 @@ suite('p5.RendererGL', function() {
assert.deepEqual(myp5.get(5, 5), [255, 0, 0, 255]);
});
- test('textures remain bound after each draw call', function() {
+ test('textures remain bound after each draw call', function () {
myp5.createCanvas(20, 10, myp5.WEBGL);
myp5.background(255);
@@ -169,7 +171,7 @@ suite('p5.RendererGL', function() {
myp5.noStroke();
myShader.setUniform('myTex', tex);
- myp5.translate(-myp5.width/2, -myp5.height/2);
+ myp5.translate(-myp5.width / 2, -myp5.height / 2);
myp5.rectMode(myp5.CORNER);
// Draw once to the left
@@ -183,7 +185,7 @@ suite('p5.RendererGL', function() {
assert.deepEqual(myp5.get(15, 5), [255, 0, 0, 255]);
});
- test('texture() does not remain bound', function() {
+ test('texture() does not remain bound', function () {
myp5.createCanvas(20, 10, myp5.WEBGL);
myp5.background(255);
@@ -209,7 +211,7 @@ suite('p5.RendererGL', function() {
myp5.pop();
});
- test('user-set uSampler on custom shader is not overridden', function() {
+ test('user-set uSampler on custom shader is not overridden', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
const myShader = myp5.createFilterShader(`precision highp float;
@@ -233,8 +235,8 @@ void main() {
});
});
- suite('default stroke shader', function() {
- test('check activate and deactivating fill and stroke', function() {
+ suite('default stroke shader', function () {
+ test('check activate and deactivating fill and stroke', function () {
myp5.noStroke();
assert(
!myp5._renderer.states.strokeColor,
@@ -256,15 +258,15 @@ void main() {
);
});
- test('coplanar strokes match 2D', function() {
- const getColors = function(mode) {
+ test('coplanar strokes match 2D', function () {
+ const getColors = function (mode) {
myp5.createCanvas(20, 20, mode);
myp5.pixelDensity(1);
myp5.background(255);
myp5.strokeCap(myp5.SQUARE);
myp5.strokeJoin(myp5.MITER);
if (mode === myp5.WEBGL) {
- myp5.translate(-myp5.width/2, -myp5.height/2);
+ myp5.translate(-myp5.width / 2, -myp5.height / 2);
}
myp5.stroke('black');
myp5.strokeWeight(4);
@@ -289,11 +291,11 @@ void main() {
});
});
- suite('filter shader', function() {
+ suite('filter shader', function () {
let frag;
let notAllBlack;
- beforeAll(function() {
+ beforeAll(function () {
frag = `precision highp float;
varying vec2 vTexCoord;
@@ -315,9 +317,11 @@ void main() {
// black/white canvas could be an indicator of failed shader logic
let val = invert ? 255 : 0;
for (let i = 0; i < pixels.length; i++) {
- if (pixels[i] !== val ||
- pixels[i+1] !== val ||
- pixels[i+2] !== val) {
+ if (
+ pixels[i] !== val ||
+ pixels[i + 1] !== val ||
+ pixels[i + 2] !== val
+ ) {
return true;
}
}
@@ -325,7 +329,7 @@ void main() {
};
});
- suite('custom shaders', function() {
+ suite('custom shaders', function () {
function testFilterShader(target) {
const fragSrc = `precision highp float;
void main() {
@@ -335,49 +339,49 @@ void main() {
target.filter(s);
target.loadPixels();
assert.deepEqual(
- target.get(target.width/2, target.height/2),
+ target.get(target.width / 2, target.height / 2),
[255, 255, 0, 255]
);
}
- test('work with a 2D main canvas', function() {
+ test('work with a 2D main canvas', function () {
myp5.createCanvas(10, 10);
testFilterShader(myp5);
});
- test('work with a WebGL main canvas', function() {
+ test('work with a WebGL main canvas', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
testFilterShader(myp5);
});
- test('work with a 2D graphic', function() {
+ test('work with a 2D graphic', function () {
myp5.createCanvas(10, 10);
const graphic = myp5.createGraphics(10, 10);
testFilterShader(graphic);
});
- test('work with a WebGL graphic', function() {
+ test('work with a WebGL graphic', function () {
myp5.createCanvas(10, 10);
const graphic = myp5.createGraphics(10, 10, myp5.WEBGL);
testFilterShader(graphic);
});
});
- test('filter accepts correct params', function() {
+ test('filter accepts correct params', function () {
myp5.createCanvas(5, 5, myp5.WEBGL);
let s = myp5.createFilterShader(frag);
myp5.filter(s);
myp5.filter(myp5.POSTERIZE, 64);
});
- test('secondary graphics layer is instantiated', function() {
+ test('secondary graphics layer is instantiated', function () {
let renderer = myp5.createCanvas(5, 5, myp5.WEBGL);
let s = myp5.createFilterShader(frag);
myp5.filter(s);
assert.notStrictEqual(renderer.filterLayer, undefined);
});
- test('custom shader makes changes to main canvas', function() {
+ test('custom shader makes changes to main canvas', function () {
myp5.createCanvas(5, 5, myp5.WEBGL);
let s = myp5.createFilterShader(frag);
myp5.background('RED');
@@ -389,7 +393,7 @@ void main() {
assert.notDeepEqual(p1, p2);
});
- test('secondary graphics layer matches main canvas size', function() {
+ test('secondary graphics layer matches main canvas size', function () {
let g1 = myp5.createCanvas(5, 5, myp5.WEBGL);
let s = myp5.createFilterShader(frag);
myp5.filter(s);
@@ -409,7 +413,7 @@ void main() {
assert.equal(g1.height, 15);
});
- test('create graphics is unaffected after filter', function() {
+ test('create graphics is unaffected after filter', function () {
myp5.createCanvas(5, 5, myp5.WEBGL);
let pg = myp5.createGraphics(5, 5, myp5.WEBGL);
pg.circle(1, 1, 1);
@@ -446,7 +450,7 @@ void main() {
assert.deepEqual(p1, p2);
});
- test('stroke and other settings are unaffected after filter', function() {
+ test('stroke and other settings are unaffected after filter', function () {
let c = myp5.createCanvas(5, 5, myp5.WEBGL);
let getShapeAttributes = () => [
c.states.ellipseMode,
@@ -465,18 +469,18 @@ void main() {
assert.deepEqual(a1, a2);
});
- test('geometries added after filter do not have shader applied', function() {
+ test('geometries added after filter do not have shader applied', function () {
myp5.createCanvas(4, 4, myp5.WEBGL);
let s = myp5.createFilterShader(frag);
myp5.filter(s);
myp5.fill('RED');
myp5.noStroke();
- myp5.rect(-2,-2,2,2);
+ myp5.rect(-2, -2, 2, 2);
myp5.loadPixels();
assert.equal(myp5.pixels[0], 255);
});
- test('createFilterShader takes a custom frag shader src', function() {
+ test('createFilterShader takes a custom frag shader src', function () {
let testCreateFilterShader = () => {
myp5.createCanvas(4, 4, myp5.WEBGL);
let s = myp5.createFilterShader(frag);
@@ -485,9 +489,9 @@ void main() {
assert.doesNotThrow(testCreateFilterShader, 'this should not throw');
});
- test('filter shader works on a p5.Graphics', function() {
- myp5.createCanvas(3,3, myp5.WEBGL);
- let pg = myp5.createGraphics(3,3, myp5.WEBGL);
+ test('filter shader works on a p5.Graphics', function () {
+ myp5.createCanvas(3, 3, myp5.WEBGL);
+ let pg = myp5.createGraphics(3, 3, myp5.WEBGL);
let s = pg.createFilterShader(frag);
pg.background('RED');
pg.loadPixels();
@@ -498,9 +502,9 @@ void main() {
assert.notDeepEqual(p1, p2);
});
- test('POSTERIZE, BLUR, THRESHOLD work without supplied param', function() {
+ test('POSTERIZE, BLUR, THRESHOLD work without supplied param', function () {
let testDefaultParams = () => {
- myp5.createCanvas(3,3, myp5.WEBGL);
+ myp5.createCanvas(3, 3, myp5.WEBGL);
myp5.filter(myp5.POSTERIZE);
myp5.filter(myp5.BLUR);
myp5.filter(myp5.THRESHOLD);
@@ -508,9 +512,9 @@ void main() {
assert.doesNotThrow(testDefaultParams, 'this should not throw');
});
- test('filters make changes to canvas', function() {
- myp5.createCanvas(20,20);
- myp5.circle(10,10,12);
+ test('filters make changes to canvas', function () {
+ myp5.createCanvas(20, 20);
+ myp5.circle(10, 10, 12);
let operations = [
myp5.BLUR,
myp5.THRESHOLD,
@@ -529,10 +533,10 @@ void main() {
}
});
- test('feedback effects can be prevented (ie. clear() works)', function() {
- myp5.createCanvas(20,20);
+ test('feedback effects can be prevented (ie. clear() works)', function () {
+ myp5.createCanvas(20, 20);
let drawAndFilter = () => {
- myp5.circle(5,5,8);
+ myp5.circle(5, 5, 8);
myp5.filter(myp5.BLUR);
};
let getPixels = () => {
@@ -552,7 +556,7 @@ void main() {
assert.arrayApproximately(p1, p2, 1);
});
- test('createFilterShader() accepts shader fragments in webgl version 2', function() {
+ test('createFilterShader() accepts shader fragments in webgl version 2', function () {
myp5.createCanvas(5, 5, myp5.WEBGL);
let s = myp5.createFilterShader(`#version 300 es
precision highp float;
@@ -570,12 +574,12 @@ void main() {
myp5.filter(s);
});
- test('BLUR parameters make different output', function() {
+ test('BLUR parameters make different output', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
let startDraw = () => {
myp5.clear();
myp5.fill('RED');
- myp5.circle(0,0,8);
+ myp5.circle(0, 0, 8);
};
let getPixels = () => {
myp5.loadPixels();
@@ -590,18 +594,18 @@ void main() {
startDraw();
myp5.filter(myp5.BLUR, 50);
let p3 = getPixels();
- assert.notDeepEqual(p1,p2);
- assert.notDeepEqual(p2,p3);
+ assert.notDeepEqual(p1, p2);
+ assert.notDeepEqual(p2, p3);
});
- test('POSTERIZE parameters make different output', function() {
+ test('POSTERIZE parameters make different output', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
let startDraw = () => {
myp5.clear();
myp5.fill('CORAL');
- myp5.circle(0,0,8);
+ myp5.circle(0, 0, 8);
myp5.fill('CORNFLOWERBLUE');
- myp5.circle(2,2,8);
+ myp5.circle(2, 2, 8);
};
let getPixels = () => {
myp5.loadPixels();
@@ -613,15 +617,15 @@ void main() {
startDraw();
myp5.filter(myp5.POSTERIZE, 4);
let p2 = getPixels();
- assert.notDeepEqual(p1,p2);
+ assert.notDeepEqual(p1, p2);
});
- test('THRESHOLD parameters make different output', function() {
+ test('THRESHOLD parameters make different output', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
let startDraw = () => {
myp5.clear();
myp5.fill('RED');
- myp5.circle(0,0,8);
+ myp5.circle(0, 0, 8);
};
let getPixels = () => {
myp5.loadPixels();
@@ -633,10 +637,10 @@ void main() {
startDraw();
myp5.filter(myp5.THRESHOLD, 0.9);
let p2 = getPixels();
- assert.notDeepEqual(p1,p2);
+ assert.notDeepEqual(p1, p2);
});
- suite('external context', function() {
+ suite('external context', function () {
const cases = [
['no modification', () => {}],
['corner rectMode', () => myp5.rectMode(myp5.CORNER)],
@@ -670,21 +674,21 @@ void main() {
};
for (const filterType of ['blur', 'invert']) {
- suite(`${filterType} filter`, function() {
+ suite(`${filterType} filter`, function () {
for (const mode of ['p2d', 'webgl']) {
- suite(`${mode} mode`, function() {
+ suite(`${mode} mode`, function () {
let defaultPixels;
beforeEach(() => {
- defaultPixels = getFilteredPixels(
- 'p2d',
- () => {}, filterType
- );
+ defaultPixels = getFilteredPixels('p2d', () => {}, filterType);
});
for (const [name, initialize] of cases) {
- test(name, function() {
- const pixels =
- getFilteredPixels(mode, initialize, filterType);
+ test(name, function () {
+ const pixels = getFilteredPixels(
+ mode,
+ initialize,
+ filterType
+ );
assert.deepEqual(pixels, defaultPixels);
});
}
@@ -695,15 +699,15 @@ void main() {
});
});
- test('contours match 2D', function() {
- const getColors = function(mode) {
+ test('contours match 2D', function () {
+ const getColors = function (mode) {
myp5.createCanvas(50, 50, mode);
myp5.pixelDensity(1);
myp5.background(200);
myp5.strokeCap(myp5.SQUARE);
myp5.strokeJoin(myp5.MITER);
if (mode === myp5.WEBGL) {
- myp5.translate(-myp5.width/2, -myp5.height/2);
+ myp5.translate(-myp5.width / 2, -myp5.height / 2);
}
myp5.stroke('black');
myp5.strokeWeight(2);
@@ -737,13 +741,15 @@ void main() {
}
}
if (!ok) {
- throw new Error(`Expected match:\n\n2D: ${colors2D.img}\n\nWebGL: ${colorsGL.img}`);
+ throw new Error(
+ `Expected match:\n\n2D: ${colors2D.img}\n\nWebGL: ${colorsGL.img}`
+ );
}
});
- suite('text shader', function() {
- test.todo('rendering looks the same in WebGL1 and 2', function() {
- myp5.loadFont('/test/unit/assets/Inconsolata-Bold.ttf', function(font) {
+ suite('text shader', function () {
+ test.todo('rendering looks the same in WebGL1 and 2', function () {
+ myp5.loadFont('/test/unit/assets/Inconsolata-Bold.ttf', function (font) {
const webgl2 = myp5.createGraphics(100, 20, myp5.WEBGL);
const webgl1 = myp5.createGraphics(100, 20, myp5.WEBGL);
webgl1.setAttributes({ version: 1 }); // no longer exists ?
@@ -768,8 +774,8 @@ void main() {
});
});
- suite('push() and pop() work in WEBGL Mode', function() {
- test('push/pop and translation works as expected in WEBGL Mode', function() {
+ suite('push() and pop() work in WEBGL Mode', function () {
+ test('push/pop and translation works as expected in WEBGL Mode', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
var modelMatrixBefore = myp5._renderer.states.uModelMatrix.copy();
var viewMatrixBefore = myp5._renderer.states.uViewMatrix.copy();
@@ -781,22 +787,28 @@ void main() {
myp5.rotateX(Math.random(0, 100));
myp5.translate(20, 100, 5);
// Check if the model matrix has changed
- assert.notDeepEqual(modelMatrixBefore.mat4,
- myp5._renderer.states.uModelMatrix.mat4);
+ assert.notDeepEqual(
+ modelMatrixBefore.mat4,
+ myp5._renderer.states.uModelMatrix.mat4
+ );
// Check if the view matrix has changed
- assert.notDeepEqual(viewMatrixBefore.mat4,
- myp5._renderer.states.uViewMatrix.mat4);
+ assert.notDeepEqual(
+ viewMatrixBefore.mat4,
+ myp5._renderer.states.uViewMatrix.mat4
+ );
myp5.pop();
// Check if both the model and view matrices are restored after popping
- assert.deepEqual(modelMatrixBefore.mat4,
- myp5._renderer.states.uModelMatrix.mat4);
+ assert.deepEqual(
+ modelMatrixBefore.mat4,
+ myp5._renderer.states.uModelMatrix.mat4
+ );
assert.deepEqual(
viewMatrixBefore.mat4,
myp5._renderer.states.uViewMatrix.mat4
);
});
- test('push/pop and directionalLight() works', function() {
+ test('push/pop and directionalLight() works', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.directionalLight(255, 0, 0, 0, 0, 0);
var dirDiffuseColors =
@@ -834,7 +846,7 @@ void main() {
);
});
- test('push/pop and ambientLight() works', function() {
+ test('push/pop and ambientLight() works', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.ambientLight(100, 0, 100);
myp5.ambientLight(0, 0, 200);
@@ -846,7 +858,7 @@ void main() {
assert.deepEqual(ambColors, myp5._renderer.states.ambientLightColors);
});
- test('push/pop and pointLight() works', function() {
+ test('push/pop and pointLight() works', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.pointLight(255, 0, 0, 0, 0, 0);
var pointDiffuseColors =
@@ -877,7 +889,7 @@ void main() {
assert.deepEqual(pointLocs, myp5._renderer.states.pointLightPositions);
});
- test('push/pop and specularColor() works', function() {
+ test('push/pop and specularColor() works', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.specularColor(255, 0, 0);
var specularColors = myp5._renderer.states.specularColors.slice();
@@ -888,7 +900,7 @@ void main() {
assert.deepEqual(specularColors, myp5._renderer.states.specularColors);
});
- test('push/pop and spotLight() works', function() {
+ test('push/pop and spotLight() works', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.spotLight(255, 0, 255, 1, 2, 3, 0, 1, 0, Math.PI / 4, 7);
let spotLightDiffuseColors =
@@ -941,7 +953,7 @@ void main() {
assert.deepEqual(spotLightConc, myp5._renderer.states.spotLightConc);
});
- test('push/pop and noLights() works', function() {
+ test('push/pop and noLights() works', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.ambientLight(0, 0, 200);
var ambColors = myp5._renderer.states.ambientLightColors.slice();
@@ -955,7 +967,7 @@ void main() {
assert.deepEqual(ambColors, myp5._renderer.states.ambientLightColors);
});
- test('push/pop and texture() works', function() {
+ test('push/pop and texture() works', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
var tex1 = myp5.createGraphics(1, 1);
myp5.texture(tex1);
@@ -976,22 +988,23 @@ void main() {
myp5.metalness(100000);
myp5.sphere(50);
expect(myp5._renderer.mixedAmbientLight).to.not.deep.equal(
- myp5._renderer.states.ambientLightColors);
+ myp5._renderer.states.ambientLightColors
+ );
});
- test('specularColor transforms to fill color when metalness is applied',
- function () {
- myp5.createCanvas(100, 100, myp5.WEBGL);
- myp5.fill(0, 0, 0, 0);
- myp5.specularMaterial(255, 255, 255, 255);
- myp5.noStroke();
- myp5.metalness(100000);
- myp5.sphere(50);
- expect(myp5._renderer.mixedSpecularColor).to.deep.equal(
- myp5._renderer.states.curFillColor);
- });
+ test('specularColor transforms to fill color when metalness is applied', function () {
+ myp5.createCanvas(100, 100, myp5.WEBGL);
+ myp5.fill(0, 0, 0, 0);
+ myp5.specularMaterial(255, 255, 255, 255);
+ myp5.noStroke();
+ myp5.metalness(100000);
+ myp5.sphere(50);
+ expect(myp5._renderer.mixedSpecularColor).to.deep.equal(
+ myp5._renderer.states.curFillColor
+ );
+ });
- test('push/pop and shader() works with fill shaders by default', function() {
+ test('push/pop and shader() works with fill shaders by default', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
var fillShader1 = myp5._renderer._getLightShader();
var fillShader2 = myp5._renderer._getColorShader();
@@ -1005,7 +1018,7 @@ void main() {
assert.equal(fillShader1, myp5._renderer.states.userFillShader);
});
- test('push/pop builds/unbuilds stack properly', function() {
+ test('push/pop builds/unbuilds stack properly', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
var col1 = myp5.color(255, 0, 0);
var col2 = myp5.color(0, 255, 0);
@@ -1029,8 +1042,8 @@ void main() {
});
});
- suite('applying cameras', function() {
- test('changing cameras keeps transforms', function() {
+ suite('applying cameras', function () {
+ test('changing cameras keeps transforms', function () {
myp5.createCanvas(50, 50, myp5.WEBGL);
const origModelMatrix = myp5._renderer.states.uModelMatrix.copy();
@@ -1072,8 +1085,8 @@ void main() {
});
});
- suite('materials', function() {
- test('ambient color defaults to the fill color', function() {
+ suite('materials', function () {
+ test('ambient color defaults to the fill color', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.noStroke();
myp5.lights();
@@ -1085,7 +1098,7 @@ void main() {
expect(pixel[2]).to.equal(0);
});
- test('ambient color can be set manually', function() {
+ test('ambient color can be set manually', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.noStroke();
myp5.lights();
@@ -1098,7 +1111,7 @@ void main() {
expect(pixel[2]).to.equal(128);
});
- test('specular is not lost by texture()', function() {
+ test('specular is not lost by texture()', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
const tex = myp5.createGraphics(100, 100);
tex.background(64);
@@ -1113,7 +1126,7 @@ void main() {
assert.deepEqual(pixel, [184, 184, 184, 255]);
});
- test('specularMaterial() does not kill texture', function() {
+ test('specularMaterial() does not kill texture', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
const tex = myp5.createGraphics(100, 100);
tex.background(64);
@@ -1128,7 +1141,7 @@ void main() {
assert.deepEqual(pixel, [184, 184, 184, 255]);
});
- test('ambientMaterial() does not kill texture', function() {
+ test('ambientMaterial() does not kill texture', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
const tex = myp5.createGraphics(100, 100);
tex.background(64);
@@ -1143,7 +1156,7 @@ void main() {
assert.deepEqual(pixel, [88, 88, 88, 255]);
});
- test('emissiveMaterial() does not kill texture', function() {
+ test('emissiveMaterial() does not kill texture', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
const tex = myp5.createGraphics(100, 100);
tex.background(64);
@@ -1159,8 +1172,8 @@ void main() {
});
});
- suite('loadpixels()', function() {
- test('loadPixels color check', function() {
+ suite('loadpixels()', function () {
+ test('loadPixels color check', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.background(0, 100, 0);
myp5.loadPixels();
@@ -1169,7 +1182,7 @@ void main() {
assert.deepEqual(pixels[3], 255);
});
- test('get() singlePixel color and size, with loadPixels', function() {
+ test('get() singlePixel color and size, with loadPixels', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.background(100, 115, 100);
myp5.loadPixels();
@@ -1178,10 +1191,10 @@ void main() {
assert.isTrue(img.length === 4);
});
- test('updatePixels() matches 2D mode', function() {
+ test('updatePixels() matches 2D mode', function () {
myp5.createCanvas(20, 20);
myp5.pixelDensity(1);
- const getColors = function(mode) {
+ const getColors = function (mode) {
const g = myp5.createGraphics(20, 20, mode);
g.pixelDensity(1);
g.background(255);
@@ -1215,20 +1228,20 @@ void main() {
});
});
- suite('get()', function() {
+ suite('get()', function () {
var img;
- test('get() size check', function() {
+ test('get() size check', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
img = myp5.get();
assert.deepEqual(img.width, myp5.width);
});
- test('get() can create p5.Image', function() {
+ test('get() can create p5.Image', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
assert.isTrue(img instanceof p5.Image);
});
- test('get() singlePixel color and size', function() {
+ test('get() singlePixel color and size', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.background(100, 115, 100);
img = myp5.get(0, 0);
@@ -1241,10 +1254,10 @@ void main() {
});
});
- suite('GL Renderer clear()', function() {
+ suite('GL Renderer clear()', function () {
var pg;
var pixel;
- test('webgl graphics background draws into webgl canvas', function() {
+ test('webgl graphics background draws into webgl canvas', function () {
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.background(0, 255, 255, 255);
pg = myp5.createGraphics(25, 50, myp5.WEBGL);
@@ -1254,7 +1267,7 @@ void main() {
assert.deepEqual(pixel, [0, 0, 0, 255]);
});
- test('transparent GL graphics with GL canvas', function() {
+ test('transparent GL graphics with GL canvas', function () {
myp5.createCanvas(50, 50, myp5.WEBGL);
pg = myp5.createGraphics(25, 50, myp5.WEBGL);
myp5.background(0, 255, 255);
@@ -1264,7 +1277,7 @@ void main() {
assert.deepEqual(pixel, [0, 255, 255, 255]);
});
- test('clear color with rgba arguments', function() {
+ test('clear color with rgba arguments', function () {
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.clear(1, 0, 0, 1);
pixel = myp5.get(0, 0);
@@ -1275,7 +1288,7 @@ void main() {
assert.deepEqual(pixel, [255, 0, 0, 255]);
});
- test('semi-transparent GL graphics with GL canvas', function() {
+ test('semi-transparent GL graphics with GL canvas', function () {
myp5.createCanvas(50, 50, myp5.WEBGL);
pg = myp5.createGraphics(25, 50, myp5.WEBGL);
myp5.background(0, 255, 255);
@@ -1285,7 +1298,7 @@ void main() {
assert.deepEqual(pixel, [39, 194, 194, 255]);
});
- test('webgl graphics background draws into 2D canvas', function() {
+ test('webgl graphics background draws into 2D canvas', function () {
myp5.createCanvas(50, 50);
myp5.background(0, 255, 255, 255);
pg = myp5.createGraphics(25, 50, myp5.WEBGL);
@@ -1295,7 +1308,7 @@ void main() {
assert.deepEqual(pixel, [0, 0, 0, 255]);
});
- test('transparent GL graphics with 2D canvas', function() {
+ test('transparent GL graphics with 2D canvas', function () {
myp5.createCanvas(50, 50);
pg = myp5.createGraphics(25, 50, myp5.WEBGL);
myp5.background(0, 255, 255);
@@ -1305,7 +1318,7 @@ void main() {
assert.deepEqual(pixel, [0, 255, 255, 255]);
});
- test('semi-transparent GL graphics with 2D canvas', function() {
+ test('semi-transparent GL graphics with 2D canvas', function () {
myp5.createCanvas(50, 50);
pg = myp5.createGraphics(25, 50, myp5.WEBGL);
myp5.background(0, 255, 255);
@@ -1316,14 +1329,14 @@ void main() {
});
});
- suite('background()', function() {
+ suite('background()', function () {
function assertAllPixelsAreColor(target, r, g, b, a) {
target.loadPixels();
const expectedPixels = [];
for (let i = 0; i < target.width * target.height; i++) {
expectedPixels.push(r, g, b, a);
}
- assert.deepEqual([ ...target.pixels ], expectedPixels);
+ assert.deepEqual([...target.pixels], expectedPixels);
}
function testDepthGetsCleared(target) {
@@ -1345,24 +1358,24 @@ void main() {
assertAllPixelsAreColor(target, 0, 0, 255, 255);
}
- test('background() resets the depth buffer of the main canvas', function() {
+ test('background() resets the depth buffer of the main canvas', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
testDepthGetsCleared(myp5);
});
- test('background() resets the depth buffer of p5.Graphics', function() {
+ test('background() resets the depth buffer of p5.Graphics', function () {
const graphics = myp5.createGraphics(10, 10, myp5.WEBGL);
testDepthGetsCleared(graphics);
});
});
- suite('blendMode()', function() {
- var testBlend = function(mode, intended) {
+ suite('blendMode()', function () {
+ var testBlend = function (mode, intended) {
myp5.blendMode(mode);
assert.deepEqual(intended, myp5._renderer.states.curBlendMode);
};
- test('blendMode sets _curBlendMode correctly', function() {
+ test('blendMode sets _curBlendMode correctly', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
testBlend(myp5.ADD, myp5.ADD);
testBlend(myp5.REPLACE, myp5.REPLACE);
@@ -1374,7 +1387,7 @@ void main() {
testBlend(myp5.DARKEST, myp5.DARKEST);
});
- test('blendMode doesnt change when mode unavailable in 3D', function() {
+ test('blendMode doesnt change when mode unavailable in 3D', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
myp5.blendMode(myp5.DARKEST);
testBlend(myp5.BURN, myp5.DARKEST);
@@ -1384,7 +1397,7 @@ void main() {
testBlend(myp5.OVERLAY, myp5.DARKEST);
});
- var mixAndReturn = function(mode, bgCol) {
+ var mixAndReturn = function (mode, bgCol) {
myp5.background(bgCol);
myp5.blendMode(mode);
myp5.fill(255, 0, 0, 122);
@@ -1394,7 +1407,7 @@ void main() {
return myp5.get(5, 5);
};
- test('blendModes change pixel colors as expected', function() {
+ test('blendModes change pixel colors as expected', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.noStroke();
assert.deepEqual([122, 0, 122, 255], mixAndReturn(myp5.ADD, 0));
@@ -1409,12 +1422,12 @@ void main() {
assert.deepEqual([0, 0, 0, 255], mixAndReturn(myp5.DARKEST, 255));
});
- test('blendModes match 2D mode', function() {
+ test('blendModes match 2D mode', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
const ref = myp5.createGraphics(myp5.width, myp5.height);
ref.translate(ref.width / 2, ref.height / 2); // Match WebGL mode
- const testBlend = function(target, colorA, colorB, mode) {
+ const testBlend = function (target, colorA, colorB, mode) {
target.clear();
target.push();
target.background(0);
@@ -1426,11 +1439,13 @@ void main() {
target.fill(colorB);
target.rect(0, 0, target.width, target.height);
target.pop();
- console.log(`${colorA} ${mode} ${colorB}: ` + target.canvas.toDataURL());
+ console.log(
+ `${colorA} ${mode} ${colorB}: ` + target.canvas.toDataURL()
+ );
return target.get(0, 0);
};
- const assertSameIn2D = function(colorA, colorB, mode) {
+ const assertSameIn2D = function (colorA, colorB, mode) {
const refColor = testBlend(myp5, colorA, colorB, mode);
const webglColor = testBlend(ref, colorA, colorB, mode);
// console.log(`Blending ${colorA} with ${colorB} using ${mode}: ${JSON.stringify(refColor)}, ${JSON.stringify(webglColor)}`)
@@ -1442,7 +1457,7 @@ void main() {
);
};
- for (const alpha of [1, 200/255]) {
+ for (const alpha of [1, 200 / 255]) {
const red = myp5.color('#F53');
const blue = myp5.color('#13F');
red.setAlpha(alpha);
@@ -1456,12 +1471,16 @@ void main() {
}
});
- test('blendModes are included in push/pop', function() {
+ test('blendModes are included in push/pop', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.blendMode(myp5.MULTIPLY);
myp5.push();
myp5.blendMode(myp5.ADD);
- assert.equal(myp5._renderer.states.curBlendMode, myp5.ADD, 'Changed to ADD');
+ assert.equal(
+ myp5._renderer.states.curBlendMode,
+ myp5.ADD,
+ 'Changed to ADD'
+ );
myp5.pop();
assert.equal(
myp5._renderer.states.curBlendMode,
@@ -1470,7 +1489,7 @@ void main() {
);
});
- test('blendModes are applied to point drawing', function() {
+ test('blendModes are applied to point drawing', function () {
myp5.createCanvas(32, 32, myp5.WEBGL);
myp5.background(0);
myp5.blendMode(myp5.ADD);
@@ -1482,7 +1501,7 @@ void main() {
assert.deepEqual(myp5.get(16, 16), [255, 0, 255, 255]);
});
- test('transparency works the same with per-vertex colors', function() {
+ test('transparency works the same with per-vertex colors', function () {
myp5.createCanvas(20, 20, myp5.WEBGL);
myp5.noStroke();
@@ -1507,8 +1526,8 @@ void main() {
});
});
- suite('BufferDef', function() {
- test('render buffer properties are correctly set', function() {
+ suite('BufferDef', function () {
+ test('render buffer properties are correctly set', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.fill(255);
@@ -1535,23 +1554,21 @@ void main() {
// + 6 verts per line cap x0 (1 quad each)
// = 54
assert.equal(geom.lineVertices.length, 54 * 3);
-
});
});
- suite('tint() in WEBGL mode', function() {
- test('default tint value', function() {
+ suite('tint() in WEBGL mode', function () {
+ test('default tint value', function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
assert.deepEqual(
- myp5._renderer.states.tint?._getRGBA([255, 255, 255, 255]) ?? [255, 255, 255, 255],
+ myp5._renderer.states.tint?._getRGBA([255, 255, 255, 255]) ?? [
+ 255, 255, 255, 255
+ ],
[255, 255, 255, 255]
);
});
-
-
- test('tint value is modified correctly when tint() is called', function() {
-
+ test('tint value is modified correctly when tint() is called', function () {
function assertColorEq(tint, colArray) {
assert.deepEqual(tint._getRGBA([255, 255, 255, 255]), colArray);
}
@@ -1592,28 +1609,30 @@ void main() {
assertColorEq(myp5._renderer.states.tint, [255, 204, 0, 255]);
});
- test('tint should be reset after draw loop', function() {
- return new Promise(function(resolve, reject) {
- new p5(function(p) {
- p.setup = function() {
+ test('tint should be reset after draw loop', function () {
+ return new Promise(function (resolve, reject) {
+ new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, myp5.WEBGL);
};
- p.draw = function() {
+ p.draw = function () {
if (p.frameCount === 2) {
resolve(p._renderer.states.tint);
}
p.tint(0, 153, 204, 126);
};
});
- }).then(function(_tint) {
- assert.deepEqual(_tint?._getRGBA([255, 255, 255, 255]) ?? [255, 255, 255, 255],
- [255, 255, 255, 255]);
+ }).then(function (_tint) {
+ assert.deepEqual(
+ _tint?._getRGBA([255, 255, 255, 255]) ?? [255, 255, 255, 255],
+ [255, 255, 255, 255]
+ );
});
});
});
- suite('beginShape() in WEBGL mode', function() {
- test('QUADS mode converts into triangles', function() {
+ suite('beginShape() in WEBGL mode', function () {
+ test('QUADS mode converts into triangles', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.textureMode(myp5.NORMAL);
myp5.beginShape(myp5.QUADS);
@@ -1665,7 +1684,7 @@ void main() {
renderer.shapeBuilder.geometry.vertices.length,
expectedVerts.length
);
- expectedVerts.forEach(function([x, y, z], i) {
+ expectedVerts.forEach(function ([x, y, z], i) {
assert.equal(renderer.shapeBuilder.geometry.vertices[i].x, x);
assert.equal(renderer.shapeBuilder.geometry.vertices[i].y, y);
assert.equal(renderer.shapeBuilder.geometry.vertices[i].z, z);
@@ -1733,14 +1752,14 @@ void main() {
renderer.shapeBuilder.geometry.vertexNormals.length,
expectedNormals.length
);
- expectedNormals.forEach(function([x, y, z], i) {
+ expectedNormals.forEach(function ([x, y, z], i) {
assert.equal(renderer.shapeBuilder.geometry.vertexNormals[i].x, x);
assert.equal(renderer.shapeBuilder.geometry.vertexNormals[i].y, y);
assert.equal(renderer.shapeBuilder.geometry.vertexNormals[i].z, z);
});
});
- test('QUADS mode makes edges for quad outlines', function() {
+ test('QUADS mode makes edges for quad outlines', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.beginShape(myp5.QUADS);
myp5.vertex(0, 0);
@@ -1757,7 +1776,7 @@ void main() {
assert.equal(renderer.shapeBuilder.geometry.edges.length, 8);
});
- test('QUAD_STRIP mode makes edges for strip outlines', function() {
+ test('QUAD_STRIP mode makes edges for strip outlines', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.beginShape(myp5.QUAD_STRIP);
myp5.vertex(0, 0);
@@ -1774,7 +1793,7 @@ void main() {
assert.equal(renderer.shapeBuilder.geometry.edges.length, 10);
});
- test('TRIANGLE_FAN mode makes edges for each triangle', function() {
+ test('TRIANGLE_FAN mode makes edges for each triangle', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
// x
// | \
@@ -1792,7 +1811,7 @@ void main() {
assert.equal(renderer.shapeBuilder.geometry.edges.length, 7);
});
- test('PATH preserves vertex data', function() {
+ test('PATH preserves vertex data', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.textureMode(myp5.NORMAL);
@@ -1867,35 +1886,23 @@ void main() {
[1, 1, 1]
);
- assert.deepEqual(renderer.shapeBuilder.geometry.aCustomSrc, [
- 1, 0, 0,
- 0, 0, 1,
- 1, 1, 1,
- 0, 0, 1,
- 1, 0, 0,
- 0, 1, 0
- ]);
-
- assert.deepEqual(renderer.shapeBuilder.geometry.vertexColors, [
- 1, 0, 0, 1,
- 0, 0, 1, 1,
- 1, 1, 1, 1,
- 0, 0, 1, 1,
- 1, 0, 0, 1,
- 0, 1, 0, 1
- ]);
-
- assert.deepEqual(renderer.shapeBuilder.geometry.uvs, [
- 1, 0,
- 0, 1,
- 0, 0,
- 0, 1,
- 1, 0,
- 1, 1
- ]);
- });
-
- test('PATH does not affect stroke colors', function() {
+ assert.deepEqual(
+ renderer.shapeBuilder.geometry.aCustomSrc,
+ [1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0]
+ );
+
+ assert.deepEqual(
+ renderer.shapeBuilder.geometry.vertexColors,
+ [1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1]
+ );
+
+ assert.deepEqual(
+ renderer.shapeBuilder.geometry.uvs,
+ [1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1]
+ );
+ });
+
+ test('PATH does not affect stroke colors', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.textureMode(myp5.NORMAL);
@@ -1912,16 +1919,13 @@ void main() {
myp5.endShape(myp5.CLOSE);
// Vertex stroke colors are not run through libtess
- assert.deepEqual(renderer.shapeBuilder.geometry.vertexStrokeColors, [
- 1, 1, 1, 1,
- 1, 0, 0, 1,
- 0, 1, 0, 1,
- 0, 0, 1, 1,
- 1, 1, 1, 1
- ]);
+ assert.deepEqual(
+ renderer.shapeBuilder.geometry.vertexStrokeColors,
+ [1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1]
+ );
});
- test('PATH does not affect texture coordinates', function() {
+ test('PATH does not affect texture coordinates', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
const texture = new p5.Image(25, 25);
@@ -1936,18 +1940,17 @@ void main() {
myp5.endShape(myp5.CLOSE);
// UVs are correctly translated through libtess
- assert.deepEqual(renderer.shapeBuilder.geometry.uvs, [
- 1, 0,
- 0, 1,
- 0, 0,
+ assert.deepEqual(
+ renderer.shapeBuilder.geometry.uvs,
+ [
+ 1, 0, 0, 1, 0, 0,
- 0, 1,
- 1, 0,
- 1, 1
- ]);
+ 0, 1, 1, 0, 1, 1
+ ]
+ );
});
- test('PATH interpolates vertex data at intersections', function() {
+ test('PATH interpolates vertex data at intersections', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
// Hourglass shape:
@@ -2028,26 +2031,21 @@ void main() {
[1, -1, 1]
);
- assert.deepEqual(renderer.shapeBuilder.geometry.vertexColors, [
- 0.5, 0.5, 0.5, 1,
- 0, 0, 1, 1,
- 1, 1, 1, 1,
- 0, 1, 0, 1,
- 0.5, 0.5, 0.5, 1,
- 1, 0, 0, 1
- ]);
+ assert.deepEqual(
+ renderer.shapeBuilder.geometry.vertexColors,
+ [
+ 0.5, 0.5, 0.5, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0.5, 0.5, 0.5,
+ 1, 1, 0, 0, 1
+ ]
+ );
- assert.deepEqual(renderer.shapeBuilder.geometry.uvs, [
- 0.5, 0.5,
- 0, 1,
- 0, 0,
- 1, 1,
- 0.5, 0.5,
- 1, 0
- ]);
+ assert.deepEqual(
+ renderer.shapeBuilder.geometry.uvs,
+ [0.5, 0.5, 0, 1, 0, 0, 1, 1, 0.5, 0.5, 1, 0]
+ );
});
- test('PATH handles vertex data perpendicular to the camera', function() {
+ test('PATH handles vertex data perpendicular to the camera', function () {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.textureMode(myp5.NORMAL);
@@ -2085,14 +2083,13 @@ void main() {
);
});
- suite('large tessellation guard', function() {
- test('prompts user before tessellating >50k vertices', function() {
+ suite('large tessellation guard', function () {
+ test('prompts user before tessellating >50k vertices', function () {
const renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false);
- const tessSpy = vi.spyOn(
- renderer.shapeBuilder,
- '_tesselateShape'
- ).mockImplementation(() => {});
+ const tessSpy = vi
+ .spyOn(renderer.shapeBuilder, '_tesselateShape')
+ .mockImplementation(() => {});
myp5.beginShape();
for (let i = 0; i < 60000; i++) {
@@ -2108,13 +2105,12 @@ void main() {
tessSpy.mockRestore();
});
- test('only prompts once when user approves large tessellation', function() {
+ test('only prompts once when user approves large tessellation', function () {
const renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true);
- const tessSpy = vi.spyOn(
- renderer.shapeBuilder,
- '_tesselateShape'
- ).mockImplementation(() => {});
+ const tessSpy = vi
+ .spyOn(renderer.shapeBuilder, '_tesselateShape')
+ .mockImplementation(() => {});
myp5.beginShape();
for (let i = 0; i < 60000; i++) {
@@ -2137,13 +2133,12 @@ void main() {
tessSpy.mockRestore();
});
- test('skips prompt when p5.disableFriendlyErrors is true', function() {
+ test('skips prompt when p5.disableFriendlyErrors is true', function () {
const renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false);
- const tessSpy = vi.spyOn(
- renderer.shapeBuilder,
- '_tesselateShape'
- ).mockImplementation(() => {});
+ const tessSpy = vi
+ .spyOn(renderer.shapeBuilder, '_tesselateShape')
+ .mockImplementation(() => {});
p5.disableFriendlyErrors = true;
myp5.beginShape();
@@ -2160,7 +2155,7 @@ void main() {
tessSpy.mockRestore();
});
- test('works normally for <50k vertices', function() {
+ test('works normally for <50k vertices', function () {
const renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false);
@@ -2178,8 +2173,8 @@ void main() {
});
});
- suite('color interpolation', function() {
- test('strokes should interpolate colors between vertices', function() {
+ suite('color interpolation', function () {
+ test('strokes should interpolate colors between vertices', function () {
const renderer = myp5.createCanvas(512, 4, myp5.WEBGL);
// far left color: (242, 236, 40)
@@ -2199,7 +2194,7 @@ void main() {
assert.deepEqual(myp5.get(511, 2), [42, 36, 240, 255]);
});
- test('geometry without stroke colors use curStrokeColor', function() {
+ test('geometry without stroke colors use curStrokeColor', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
myp5.background(255);
myp5.fill(255);
@@ -2212,28 +2207,45 @@ void main() {
assert.deepEqual(myp5.get(128, 0), [0, 0, 0, 255]);
});
- test('geometry with stroke colors use their colors', function() {
+ test('geometry with stroke colors use their colors', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
- const myGeom = new p5.Geometry(1, 1, function() {
- this.gid = 'strokeColorTest';
- this.vertices.push(myp5.createVector(-128, -128));
- this.vertices.push(myp5.createVector(-128, 128));
- this.vertices.push(myp5.createVector(128, 128));
- this.vertices.push(myp5.createVector(128, -128));
- this.faces.push([0, 1, 2]);
- this.faces.push([0, 2, 3]);
- this.edges.push([0, 1]);
- this.edges.push([1, 2]);
- this.edges.push([2, 3]);
- this.edges.push([3, 0]);
- this.vertexStrokeColors.push(
- 0, 0, 0, 1,
- 1, 0, 0, 1,
- 0, 0, 1, 1,
- 0, 1, 0, 1
- );
- this._edgesToVertices();
- }, myp5._renderer);
+ const myGeom = new p5.Geometry(
+ 1,
+ 1,
+ function () {
+ this.gid = 'strokeColorTest';
+ this.vertices.push(myp5.createVector(-128, -128));
+ this.vertices.push(myp5.createVector(-128, 128));
+ this.vertices.push(myp5.createVector(128, 128));
+ this.vertices.push(myp5.createVector(128, -128));
+ this.faces.push([0, 1, 2]);
+ this.faces.push([0, 2, 3]);
+ this.edges.push([0, 1]);
+ this.edges.push([1, 2]);
+ this.edges.push([2, 3]);
+ this.edges.push([3, 0]);
+ this.vertexStrokeColors.push(
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1
+ );
+ this._edgesToVertices();
+ },
+ myp5._renderer
+ );
myp5.background(255);
myp5.fill(255);
myp5.strokeWeight(4);
@@ -2244,7 +2256,7 @@ void main() {
assert.deepEqual(myp5.get(128, 255), [127, 0, 128, 255]);
});
- test('immediate mode uses stroke colors', function() {
+ test('immediate mode uses stroke colors', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
myp5.background(255);
myp5.fill(255);
@@ -2265,8 +2277,8 @@ void main() {
});
});
- suite('interpolation of vertex colors', function(){
- test('immediate mode uses vertex colors (noLight)', function() {
+ suite('interpolation of vertex colors', function () {
+ test('immediate mode uses vertex colors (noLight)', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
// upper color: (200, 0, 0, 255);
@@ -2288,7 +2300,7 @@ void main() {
assert.deepEqual(myp5.get(128, 128), [100, 0, 100, 255]);
});
- test('immediate mode uses vertex colors (light)', function() {
+ test('immediate mode uses vertex colors (light)', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
myp5.directionalLight(255, 255, 255, 0, 0, -1);
@@ -2310,7 +2322,7 @@ void main() {
assert.deepEqual(myp5.get(128, 128), [73, 0, 73, 255]);
});
- test('geom without vertex colors use curFillCol (noLight)', function() {
+ test('geom without vertex colors use curFillCol (noLight)', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
// expected center color is curFillColor.
@@ -2323,7 +2335,7 @@ void main() {
assert.deepEqual(myp5.get(128, 128), [200, 0, 200, 255]);
});
- test('geom without vertex colors use curFillCol (light)', function() {
+ test('geom without vertex colors use curFillCol (light)', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
myp5.directionalLight(255, 255, 255, 0, 0, -1);
@@ -2338,14 +2350,14 @@ void main() {
assert.deepEqual(myp5.get(128, 128), [146, 0, 146, 255]);
});
- test('geom with vertex colors use their color (noLight)', function() {
+ test('geom with vertex colors use their color (noLight)', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
// upper color: (200, 0, 0, 255);
// lower color: (0, 0, 200, 255);
// expected center color: (100, 0, 100, 255);
- const myGeom = new p5.Geometry(1, 1, function() {
+ const myGeom = new p5.Geometry(1, 1, function () {
this.gid = 'vertexColorTestWithNoLights';
this.vertices.push(myp5.createVector(-128, -128));
this.vertices.push(myp5.createVector(128, -128));
@@ -2354,10 +2366,22 @@ void main() {
this.faces.push([0, 1, 2]);
this.faces.push([0, 2, 3]);
this.vertexColors.push(
- 200/255, 0, 0, 1,
- 200/255, 0, 0, 1,
- 0, 0, 200/255, 1,
- 0, 0, 200/255, 1
+ 200 / 255,
+ 0,
+ 0,
+ 1,
+ 200 / 255,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 200 / 255,
+ 1,
+ 0,
+ 0,
+ 200 / 255,
+ 1
);
this.computeNormals();
});
@@ -2369,10 +2393,10 @@ void main() {
assert.deepEqual(myp5.get(128, 128), [100, 0, 100, 255]);
});
- test('geom with vertex colors use their color (light)', function() {
+ test('geom with vertex colors use their color (light)', function () {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);
- const myGeom = new p5.Geometry(1, 1, function() {
+ const myGeom = new p5.Geometry(1, 1, function () {
this.gid = 'vertexColorTestWithLighs';
this.vertices.push(myp5.createVector(-128, -128));
this.vertices.push(myp5.createVector(128, -128));
@@ -2381,10 +2405,22 @@ void main() {
this.faces.push([0, 1, 2]);
this.faces.push([0, 2, 3]);
this.vertexColors.push(
- 200/255, 0, 0, 1,
- 200/255, 0, 0, 1,
- 0, 0, 200/255, 1,
- 0, 0, 200/255, 1
+ 200 / 255,
+ 0,
+ 0,
+ 1,
+ 200 / 255,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 200 / 255,
+ 1,
+ 0,
+ 0,
+ 200 / 255,
+ 1
);
this.computeNormals();
});
@@ -2400,21 +2436,26 @@ void main() {
});
});
- suite('Test for register availability', function() {
- test('register enable/disable flag test', function() {
+ suite('Test for register availability', function () {
+ test('register enable/disable flag test', function () {
const renderer = myp5.createCanvas(16, 16, myp5.WEBGL);
// geometry without aTexCoord.
- const myGeom = new p5.Geometry(1, 1, function() {
- this.gid = 'registerEnabledTest';
- this.vertices.push(myp5.createVector(-8, -8));
- this.vertices.push(myp5.createVector(8, -8));
- this.vertices.push(myp5.createVector(8, 8));
- this.vertices.push(myp5.createVector(-8, 8));
- this.faces.push([0, 1, 2]);
- this.faces.push([0, 2, 3]);
- this.computeNormals();
- }, myp5._renderer);
+ const myGeom = new p5.Geometry(
+ 1,
+ 1,
+ function () {
+ this.gid = 'registerEnabledTest';
+ this.vertices.push(myp5.createVector(-8, -8));
+ this.vertices.push(myp5.createVector(8, -8));
+ this.vertices.push(myp5.createVector(8, 8));
+ this.vertices.push(myp5.createVector(-8, 8));
+ this.faces.push([0, 1, 2]);
+ this.faces.push([0, 2, 3]);
+ this.computeNormals();
+ },
+ myp5._renderer
+ );
myp5.fill(255);
myp5.noStroke();
@@ -2437,8 +2478,8 @@ void main() {
});
});
- suite('setAttributes', function() {
- test('It leaves a reference to the correct canvas', function() {
+ suite('setAttributes', function () {
+ test('It leaves a reference to the correct canvas', function () {
const renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
assert.equal(myp5.canvas, renderer.canvas);
@@ -2447,8 +2488,8 @@ void main() {
});
});
- suite('instancing', function() {
- test('instanced', function() {
+ suite('instancing', function () {
+ test('instanced', function () {
let defShader;
const vertShader = `#version 300 es
@@ -2495,14 +2536,8 @@ void main() {
myp5.shader(defShader);
{
// Check to make sure that pixels are empty first
- assert.deepEqual(
- myp5.get(0, 0),
- [0, 0, 0, 255]
- );
- assert.deepEqual(
- myp5.get(1, 0),
- [0, 0, 0, 255]
- );
+ assert.deepEqual(myp5.get(0, 0), [0, 0, 0, 255]);
+ assert.deepEqual(myp5.get(1, 0), [0, 0, 0, 255]);
const siz = 1;
myp5.translate(-myp5.width / 2, -myp5.height / 2);
@@ -2514,32 +2549,26 @@ void main() {
myp5.endShape(myp5.CLOSE, 2);
// check the pixels after instancing to make sure that they're the correct color
- assert.deepEqual(
- myp5.get(0, 0),
- [255, 0, 0, 255]
- );
- assert.deepEqual(
- myp5.get(1, 0),
- [255, 0, 0, 255]
- );
+ assert.deepEqual(myp5.get(0, 0), [255, 0, 0, 255]);
+ assert.deepEqual(myp5.get(1, 0), [255, 0, 0, 255]);
}
myp5.resetShader();
});
});
- suite('instances() API', function() {
+ suite('instances() API', function () {
let drawSpy;
- beforeEach(function() {
+ beforeEach(function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
drawSpy = vi.spyOn(myp5._renderer, '_drawGeometry');
});
- afterEach(function() {
+ afterEach(function () {
vi.restoreAllMocks();
});
- test('instances(5).sphere() sets correct instanceCount on draw and clears it', function() {
+ test('instances(5).sphere() sets correct instanceCount on draw and clears it', function () {
myp5.instances(5).sphere(10);
expect(drawSpy).toHaveBeenCalled();
@@ -2549,7 +2578,7 @@ void main() {
assert.isUndefined(myp5._renderer._instanceCount);
});
- test('instances(5).box() sets correct instanceCount on draw and clears it', function() {
+ test('instances(5).box() sets correct instanceCount on draw and clears it', function () {
myp5.instances(5).box(10);
expect(drawSpy).toHaveBeenCalled();
@@ -2559,7 +2588,7 @@ void main() {
assert.isUndefined(myp5._renderer._instanceCount);
});
- test('instances(5).model(geom) uses instances count', function() {
+ test('instances(5).model(geom) uses instances count', function () {
const geom = new p5.Geometry();
geom.gid = 'instances_model_test';
geom.vertices.push(myp5.createVector(0, 0, 0));
@@ -2575,7 +2604,7 @@ void main() {
assert.isUndefined(myp5._renderer._instanceCount);
});
- test('instances(10).model(geom, 1) has explicit-count precedence', function() {
+ test('instances(10).model(geom, 1) has explicit-count precedence', function () {
const geom = new p5.Geometry();
geom.gid = 'instances_precedence_test';
geom.vertices.push(myp5.createVector(0, 0, 0));
@@ -2592,15 +2621,15 @@ void main() {
});
});
- suite('clip()', function() {
+ suite('clip()', function () {
//let myp5;
function getClippedPixels(mode, mask) {
// Clean up the last myp5 instance, since this may be called more than
// once per test
myp5.remove();
- myp5 = new p5(function(p) {
- p.setup = function() {};
- p.draw = function() {};
+ myp5 = new p5(function (p) {
+ p.setup = function () {};
+ p.draw = function () {};
});
myp5.createCanvas(50, 50, mode);
@@ -2618,7 +2647,7 @@ void main() {
return pixels.slice(start, start + 4);
}
- test('It can mask in a shape', function() {
+ test('It can mask in a shape', function () {
const mask = () => {
myp5.background(255);
myp5.noStroke();
@@ -2642,7 +2671,7 @@ void main() {
assert.deepEqual(pixels, getClippedPixels(myp5.P2D, mask));
});
- test('It can mask out a shape', function() {
+ test('It can mask out a shape', function () {
const mask = () => {
myp5.background(255);
myp5.noStroke();
@@ -2665,7 +2694,7 @@ void main() {
assert.deepEqual(pixels, getClippedPixels(myp5.P2D, mask));
});
- test('It can mask multiple shapes', function() {
+ test('It can mask multiple shapes', function () {
const mask = () => {
myp5.background(255);
myp5.noStroke();
@@ -2697,7 +2726,7 @@ void main() {
assert.deepEqual(pixels, getClippedPixels(myp5.P2D, mask));
});
- test('It can mask in a shape in a framebuffer', function() {
+ test('It can mask in a shape in a framebuffer', function () {
const mask = () => {
const fbo = myp5.createFramebuffer({ antialias: false });
myp5.background(255);
@@ -2724,395 +2753,362 @@ void main() {
assert.deepEqual(getPixel(pixels, 15, 15), [255, 0, 0, 255]);
});
- test(
- 'It can mask a separate shape in a framebuffer from the main canvas',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- myp5.noStroke();
- const fbo = myp5.createFramebuffer({ antialias: false });
- myp5.rectMode(myp5.CENTER);
- myp5.background('red');
- expect(myp5._renderer._clipDepths.length).to.equal(0);
- myp5.push();
- myp5.beginClip();
- myp5.rect(-5, -5, 20, 20);
- myp5.endClip();
- expect(myp5._renderer._clipDepths.length).to.equal(1);
-
- fbo.begin();
- myp5.beginClip();
- myp5.rect(5, 5, 20, 20);
- myp5.endClip();
- myp5.fill('blue');
- myp5.rect(0, 0, myp5.width, myp5.height);
- expect(myp5._renderer._clipDepths.length).to.equal(2);
- expect(myp5._renderer.drawTarget()).to.equal(fbo);
- expect(fbo._isClipApplied).to.equal(true);
- fbo.end();
- expect(fbo._isClipApplied).to.equal(false);
- expect(myp5._renderer._clipDepths.length).to.equal(1);
- expect(myp5._renderer.drawTarget()).to.equal(myp5._renderer);
- expect(myp5._renderer._isClipApplied).to.equal(true);
+ test('It can mask a separate shape in a framebuffer from the main canvas', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ myp5.noStroke();
+ const fbo = myp5.createFramebuffer({ antialias: false });
+ myp5.rectMode(myp5.CENTER);
+ myp5.background('red');
+ expect(myp5._renderer._clipDepths.length).to.equal(0);
+ myp5.push();
+ myp5.beginClip();
+ myp5.rect(-5, -5, 20, 20);
+ myp5.endClip();
+ expect(myp5._renderer._clipDepths.length).to.equal(1);
+
+ fbo.begin();
+ myp5.beginClip();
+ myp5.rect(5, 5, 20, 20);
+ myp5.endClip();
+ myp5.fill('blue');
+ myp5.rect(0, 0, myp5.width, myp5.height);
+ expect(myp5._renderer._clipDepths.length).to.equal(2);
+ expect(myp5._renderer.drawTarget()).to.equal(fbo);
+ expect(fbo._isClipApplied).to.equal(true);
+ fbo.end();
+ expect(fbo._isClipApplied).to.equal(false);
+ expect(myp5._renderer._clipDepths.length).to.equal(1);
+ expect(myp5._renderer.drawTarget()).to.equal(myp5._renderer);
+ expect(myp5._renderer._isClipApplied).to.equal(true);
+
+ myp5.imageMode(myp5.CENTER);
+ myp5.image(fbo, 0, 0);
+ myp5.pop();
+ expect(myp5._renderer._clipDepths.length).to.equal(0);
- myp5.imageMode(myp5.CENTER);
- myp5.image(fbo, 0, 0);
- myp5.pop();
- expect(myp5._renderer._clipDepths.length).to.equal(0);
+ // In the middle of the canvas, the framebuffer's clip and the
+ // main canvas's clip intersect, so the blue should show through
+ assert.deepEqual(
+ myp5.get(myp5.width / 2, myp5.height / 2),
+ [0, 0, 255, 255]
+ );
- // In the middle of the canvas, the framebuffer's clip and the
- // main canvas's clip intersect, so the blue should show through
+ // To either side of the center, nothing should be on top of
+ // the red background color
+ for (const side of [-1, 1]) {
assert.deepEqual(
- myp5.get(myp5.width / 2, myp5.height / 2),
- [0, 0, 255, 255]
+ myp5.get(myp5.width / 2 + side * 10, myp5.height / 2 + side * 10),
+ [255, 0, 0, 255]
);
-
- // To either side of the center, nothing should be on top of
- // the red background color
- for (const side of [-1, 1]) {
- assert.deepEqual(
- myp5.get(myp5.width / 2 + side * 10, myp5.height / 2 + side * 10),
- [255, 0, 0, 255]
- );
- }
}
- );
+ });
- test(
- 'Main canvas masks do not apply to framebuffers',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const fbo = myp5.createFramebuffer({ antialias: false });
- myp5.rectMode(myp5.CENTER);
- myp5.background('red');
- expect(myp5._renderer._stencilTestOn).to.equal(false);
- myp5.push();
- myp5.beginClip();
- myp5.rect(-20, -20, 40, 40);
- myp5.endClip();
- expect(myp5._renderer._stencilTestOn).to.equal(true);
+ test('Main canvas masks do not apply to framebuffers', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const fbo = myp5.createFramebuffer({ antialias: false });
+ myp5.rectMode(myp5.CENTER);
+ myp5.background('red');
+ expect(myp5._renderer._stencilTestOn).to.equal(false);
+ myp5.push();
+ myp5.beginClip();
+ myp5.rect(-20, -20, 40, 40);
+ myp5.endClip();
+ expect(myp5._renderer._stencilTestOn).to.equal(true);
- fbo.begin();
- expect(myp5._renderer._stencilTestOn).to.equal(false);
- myp5.noStroke();
- myp5.fill('blue');
- myp5.rect(0, 0, myp5.width, myp5.height);
- fbo.end();
+ fbo.begin();
+ expect(myp5._renderer._stencilTestOn).to.equal(false);
+ myp5.noStroke();
+ myp5.fill('blue');
+ myp5.rect(0, 0, myp5.width, myp5.height);
+ fbo.end();
- expect(myp5._renderer._stencilTestOn).to.equal(true);
- myp5.pop();
- expect(myp5._renderer._stencilTestOn).to.equal(false);
+ expect(myp5._renderer._stencilTestOn).to.equal(true);
+ myp5.pop();
+ expect(myp5._renderer._stencilTestOn).to.equal(false);
- myp5.imageMode(myp5.CENTER);
- myp5.image(fbo, 0, 0);
+ myp5.imageMode(myp5.CENTER);
+ myp5.image(fbo, 0, 0);
- // In the middle of the canvas, the framebuffer's clip and the
- // main canvas's clip intersect, so the blue should show through
- assert.deepEqual(
- myp5.get(myp5.width / 2, myp5.height / 2),
- [0, 0, 255, 255]
- );
- }
- );
+ // In the middle of the canvas, the framebuffer's clip and the
+ // main canvas's clip intersect, so the blue should show through
+ assert.deepEqual(
+ myp5.get(myp5.width / 2, myp5.height / 2),
+ [0, 0, 255, 255]
+ );
+ });
});
- suite('vertexProperty()', function() {
- test('Immediate mode data and buffers created in beginShape',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
+ suite('vertexProperty()', function () {
+ test('Immediate mode data and buffers created in beginShape', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
- myp5.noStroke();
- myp5.beginShape();
- myp5.vertexProperty('aCustom', 1);
- myp5.vertexProperty('aCustomVec3', [1, 2, 3]);
- myp5.vertex(0,0,0);
- myp5.vertex(0,1,0);
- myp5.vertex(1,1,0);
- myp5.endShape();
+ myp5.noStroke();
+ myp5.beginShape();
+ myp5.vertexProperty('aCustom', 1);
+ myp5.vertexProperty('aCustomVec3', [1, 2, 3]);
+ myp5.vertex(0, 0, 0);
+ myp5.vertex(0, 1, 0);
+ myp5.vertex(1, 1, 0);
+ myp5.endShape();
- expect(
- myp5._renderer.shapeBuilder.geometry.userVertexProperties.aCustom
- ).to.containSubset({
- name: 'aCustom',
- currentData: [1],
- dataSize: 1
- });
- expect(
- myp5._renderer.shapeBuilder.geometry.userVertexProperties.aCustomVec3
- ).to.containSubset({
- name: 'aCustomVec3',
- currentData: [1, 2, 3],
- dataSize: 3
- });
- }
- );
- test('Immediate mode data and buffers deleted after beginShape',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
+ expect(
+ myp5._renderer.shapeBuilder.geometry.userVertexProperties.aCustom
+ ).to.containSubset({
+ name: 'aCustom',
+ currentData: [1],
+ dataSize: 1
+ });
+ expect(
+ myp5._renderer.shapeBuilder.geometry.userVertexProperties.aCustomVec3
+ ).to.containSubset({
+ name: 'aCustomVec3',
+ currentData: [1, 2, 3],
+ dataSize: 3
+ });
+ });
+ test('Immediate mode data and buffers deleted after beginShape', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ myp5.beginShape();
+ myp5.vertexProperty('aCustom', 1);
+ myp5.vertexProperty('aCustomVec3', [1, 2, 3]);
+ myp5.vertex(0, 0, 0);
+ myp5.endShape();
+
+ myp5.beginShape();
+ myp5.endShape();
+ assert.isUndefined(myp5._renderer.shapeBuilder.geometry.aCustomSrc);
+ assert.isUndefined(myp5._renderer.shapeBuilder.geometry.aCustomVec3Src);
+ assert.deepEqual(
+ myp5._renderer.shapeBuilder.geometry.userVertexProperties,
+ {}
+ );
+ assert.deepEqual(myp5._renderer.buffers.user, []);
+ });
+ test('Data copied over from beginGeometry', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const myGeo = myp5.buildGeometry(() => {
myp5.beginShape();
myp5.vertexProperty('aCustom', 1);
- myp5.vertexProperty('aCustomVec3', [1,2,3]);
- myp5.vertex(0,0,0);
+ myp5.vertexProperty('aCustomVec3', [1, 2, 3]);
+ myp5.vertex(0, 1, 0);
+ myp5.vertex(-1, 0, 0);
+ myp5.vertex(1, 0, 0);
myp5.endShape();
+ });
+ assert.deepEqual(myGeo.aCustomSrc, [1, 1, 1]);
+ assert.deepEqual(myGeo.aCustomVec3Src, [1, 2, 3, 1, 2, 3, 1, 2, 3]);
+ });
+ test('Retained mode buffers are created for rendering', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
- myp5.beginShape();
- myp5.endShape();
- assert.isUndefined(myp5._renderer.shapeBuilder.geometry.aCustomSrc);
- assert.isUndefined(myp5._renderer.shapeBuilder.geometry.aCustomVec3Src);
- assert.deepEqual(
- myp5._renderer.shapeBuilder.geometry.userVertexProperties,
- {}
- );
- assert.deepEqual(myp5._renderer.buffers.user, []);
- }
- );
- test('Data copied over from beginGeometry',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const myGeo = myp5.buildGeometry(() => {
- myp5.beginShape();
- myp5.vertexProperty('aCustom', 1);
- myp5.vertexProperty('aCustomVec3', [1,2,3]);
- myp5.vertex(0,1,0);
- myp5.vertex(-1,0,0);
- myp5.vertex(1,0,0);
- myp5.endShape();
- });
- assert.deepEqual(myGeo.aCustomSrc, [1,1,1]);
- assert.deepEqual(myGeo.aCustomVec3Src, [1,2,3,1,2,3,1,2,3]);
- }
- );
- test('Retained mode buffers are created for rendering',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
-
- const prevDrawFills = myp5._renderer._drawFills;
- let called = false;
- myp5._renderer._drawFills = function(...args) {
- called = true;
- expect(myp5._renderer.buffers.user).to.containSubset([
- {
- size: 1,
- src: 'aCustomSrc',
- dst: 'aCustomBuffer',
- attr: 'aCustom'
- },
- {
- size: 3,
- src: 'aCustomVec3Src',
- dst: 'aCustomVec3Buffer',
- attr: 'aCustomVec3'
- }
- ]);
+ const prevDrawFills = myp5._renderer._drawFills;
+ let called = false;
+ myp5._renderer._drawFills = function (...args) {
+ called = true;
+ expect(myp5._renderer.buffers.user).to.containSubset([
+ {
+ size: 1,
+ src: 'aCustomSrc',
+ dst: 'aCustomBuffer',
+ attr: 'aCustom'
+ },
+ {
+ size: 3,
+ src: 'aCustomVec3Src',
+ dst: 'aCustomVec3Buffer',
+ attr: 'aCustomVec3'
+ }
+ ]);
- prevDrawFills.apply(this, args);
- };
+ prevDrawFills.apply(this, args);
+ };
- try {
- const myGeo = myp5.buildGeometry(() => {
- myp5.beginShape();
- myp5.vertexProperty('aCustom', 1);
- myp5.vertexProperty('aCustomVec3', [1,2,3]);
- myp5.vertex(0,0,0);
- myp5.vertex(1,0,0);
- myp5.vertex(1,1,0);
- myp5.endShape();
- });
- myp5.model(myGeo);
- expect(called).to.equal(true);
- } finally {
- myp5._renderer._drawFills = prevDrawFills;
- }
- }
- );
- test('Retained mode buffers deleted after rendering',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
+ try {
const myGeo = myp5.buildGeometry(() => {
myp5.beginShape();
myp5.vertexProperty('aCustom', 1);
- myp5.vertexProperty('aCustomVec3', [1,2,3]);
- myp5.vertex(0,0,0);
- myp5.vertex(1,0,0);
- myp5.vertex(1,1,0);
+ myp5.vertexProperty('aCustomVec3', [1, 2, 3]);
+ myp5.vertex(0, 0, 0);
+ myp5.vertex(1, 0, 0);
+ myp5.vertex(1, 1, 0);
myp5.endShape();
});
myp5.model(myGeo);
- assert.equal(myp5._renderer.buffers.user.length, 0);
+ expect(called).to.equal(true);
+ } finally {
+ myp5._renderer._drawFills = prevDrawFills;
}
- );
- test.skip('Friendly error if different sizes used',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const logs = [];
- const myLog = (...data) => logs.push(data.join(', '));
- const oldLog = console.log;
- console.log = myLog;
+ });
+ test('Retained mode buffers deleted after rendering', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const myGeo = myp5.buildGeometry(() => {
myp5.beginShape();
- myp5.vertexProperty('aCustom', [1,2,3]);
- myp5.vertex(0,0,0);
- myp5.vertexProperty('aCustom', [1,2]);
- myp5.vertex(1,0,0);
+ myp5.vertexProperty('aCustom', 1);
+ myp5.vertexProperty('aCustomVec3', [1, 2, 3]);
+ myp5.vertex(0, 0, 0);
+ myp5.vertex(1, 0, 0);
+ myp5.vertex(1, 1, 0);
myp5.endShape();
- console.log = oldLog;
- expect(logs.join('\n')).to.match(/Custom vertex property 'aCustom' has been set with various data sizes/);
- }
- );
- test.skip('Friendly error too many values set',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const logs = [];
- const myLog = (...data) => logs.push(data.join(', '));
- const oldLog = console.log;
- console.log = myLog;
- let myGeo = new p5.Geometry();
- myGeo.gid = 'myGeo';
- myGeo.vertices.push(new myp5.createVector(0,0,0));
- myGeo.vertices.push(new myp5.createVector(1,0,0));
- myGeo.vertices.push(new myp5.createVector(1,1,0));
- myGeo.vertexProperty('aCustom', 1);
- myGeo.vertexProperty('aCustom', 2);
- myGeo.vertexProperty('aCustom', 3);
- myGeo.vertexProperty('aCustom', 4);
- myp5.model(myGeo);
- console.log = oldLog;
- expect(logs.join('\n')).to.match(/One of the geometries has a custom vertex property 'aCustom' with more values than vertices./);
- }
- );
- test.skip('Friendly error if too few values set',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const logs = [];
- const myLog = (...data) => logs.push(data.join(', '));
- const oldLog = console.log;
- console.log = myLog;
- let myGeo = new p5.Geometry();
- myGeo.gid = 'myGeo';
- myGeo.vertices.push(new myp5.createVector(0,0,0));
- myGeo.vertices.push(new myp5.createVector(1,0,0));
- myGeo.vertices.push(new myp5.createVector(1,1,0));
- myGeo.vertexProperty('aCustom', 1);
- myp5.model(myGeo);
- console.log = oldLog;
- expect(logs.join('\n')).to.match(/One of the geometries has a custom vertex property 'aCustom' with fewer values than vertices./);
- }
- );
+ });
+ myp5.model(myGeo);
+ assert.equal(myp5._renderer.buffers.user.length, 0);
+ });
+ test.skip('Friendly error if different sizes used', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const logs = [];
+ const myLog = (...data) => logs.push(data.join(', '));
+ const oldLog = console.log;
+ console.log = myLog;
+ myp5.beginShape();
+ myp5.vertexProperty('aCustom', [1, 2, 3]);
+ myp5.vertex(0, 0, 0);
+ myp5.vertexProperty('aCustom', [1, 2]);
+ myp5.vertex(1, 0, 0);
+ myp5.endShape();
+ console.log = oldLog;
+ expect(logs.join('\n')).to.match(
+ /Custom vertex property 'aCustom' has been set with various data sizes/
+ );
+ });
+ test.skip('Friendly error too many values set', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const logs = [];
+ const myLog = (...data) => logs.push(data.join(', '));
+ const oldLog = console.log;
+ console.log = myLog;
+ let myGeo = new p5.Geometry();
+ myGeo.gid = 'myGeo';
+ myGeo.vertices.push(new myp5.createVector(0, 0, 0));
+ myGeo.vertices.push(new myp5.createVector(1, 0, 0));
+ myGeo.vertices.push(new myp5.createVector(1, 1, 0));
+ myGeo.vertexProperty('aCustom', 1);
+ myGeo.vertexProperty('aCustom', 2);
+ myGeo.vertexProperty('aCustom', 3);
+ myGeo.vertexProperty('aCustom', 4);
+ myp5.model(myGeo);
+ console.log = oldLog;
+ expect(logs.join('\n')).to.match(
+ /One of the geometries has a custom vertex property 'aCustom' with more values than vertices./
+ );
+ });
+ test.skip('Friendly error if too few values set', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const logs = [];
+ const myLog = (...data) => logs.push(data.join(', '));
+ const oldLog = console.log;
+ console.log = myLog;
+ let myGeo = new p5.Geometry();
+ myGeo.gid = 'myGeo';
+ myGeo.vertices.push(new myp5.createVector(0, 0, 0));
+ myGeo.vertices.push(new myp5.createVector(1, 0, 0));
+ myGeo.vertices.push(new myp5.createVector(1, 1, 0));
+ myGeo.vertexProperty('aCustom', 1);
+ myp5.model(myGeo);
+ console.log = oldLog;
+ expect(logs.join('\n')).to.match(
+ /One of the geometries has a custom vertex property 'aCustom' with fewer values than vertices./
+ );
+ });
});
- suite('Stencil Test Tracking', function() {
- test('Stencil test is disabled by default',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const gl = myp5._renderer.GL;
- const isEnabled = gl.isEnabled(gl.STENCIL_TEST);
+ suite('Stencil Test Tracking', function () {
+ test('Stencil test is disabled by default', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const gl = myp5._renderer.GL;
+ const isEnabled = gl.isEnabled(gl.STENCIL_TEST);
- assert.equal(isEnabled, false);
- assert.equal(myp5._renderer._userEnabledStencil, false);
- }
- );
+ assert.equal(isEnabled, false);
+ assert.equal(myp5._renderer._userEnabledStencil, false);
+ });
- test('Tracks when user manually enables stencil test',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const gl = myp5._renderer.GL;
+ test('Tracks when user manually enables stencil test', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const gl = myp5._renderer.GL;
- gl.enable(gl.STENCIL_TEST);
- assert.equal(myp5._renderer._userEnabledStencil, true);
- assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
- }
- );
+ gl.enable(gl.STENCIL_TEST);
+ assert.equal(myp5._renderer._userEnabledStencil, true);
+ assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
+ });
- test('Tracks when user manually disables stencil test',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const gl = myp5._renderer.GL;
+ test('Tracks when user manually disables stencil test', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const gl = myp5._renderer.GL;
- gl.enable(gl.STENCIL_TEST);
- gl.disable(gl.STENCIL_TEST);
+ gl.enable(gl.STENCIL_TEST);
+ gl.disable(gl.STENCIL_TEST);
- assert.equal(myp5._renderer._userEnabledStencil, false);
- assert.equal(gl.isEnabled(gl.STENCIL_TEST), false);
- }
- );
+ assert.equal(myp5._renderer._userEnabledStencil, false);
+ assert.equal(gl.isEnabled(gl.STENCIL_TEST), false);
+ });
- test('Maintains stencil test state across draw cycles when user enabled',
- function() {
- let drawCalled = false;
+ test('Maintains stencil test state across draw cycles when user enabled', function () {
+ let drawCalled = false;
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const originalDraw = myp5.draw;
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const originalDraw = myp5.draw;
- myp5.draw = function() {
- drawCalled = true;
- if (originalDraw) originalDraw.call(myp5);
- };
+ myp5.draw = function () {
+ drawCalled = true;
+ if (originalDraw) originalDraw.call(myp5);
+ };
- const gl = myp5._renderer.GL;
- gl.enable(gl.STENCIL_TEST);
- assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
- myp5.redraw();
+ const gl = myp5._renderer.GL;
+ gl.enable(gl.STENCIL_TEST);
+ assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
+ myp5.redraw();
- assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
- assert.equal(myp5._renderer._userEnabledStencil, true);
+ assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
+ assert.equal(myp5._renderer._userEnabledStencil, true);
- myp5.draw = originalDraw;
- }
- );
+ myp5.draw = originalDraw;
+ });
- test('Internal clip operations preserve user stencil test setting',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const gl = myp5._renderer.GL;
+ test('Internal clip operations preserve user stencil test setting', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const gl = myp5._renderer.GL;
- gl.enable(gl.STENCIL_TEST);
+ gl.enable(gl.STENCIL_TEST);
- myp5.push();
- myp5.clip(() => {
- myp5.rect(0, 0, 10, 10);
- });
- assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
- myp5.pop();
+ myp5.push();
+ myp5.clip(() => {
+ myp5.rect(0, 0, 10, 10);
+ });
+ assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
+ myp5.pop();
- assert.equal(myp5._renderer._userEnabledStencil, true);
- assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
- }
- );
+ assert.equal(myp5._renderer._userEnabledStencil, true);
+ assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
+ });
- test('Internal clip operations do not enable stencil test for future draw cycles',
- function() {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- const gl = myp5._renderer.GL;
+ test('Internal clip operations do not enable stencil test for future draw cycles', function () {
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ const gl = myp5._renderer.GL;
- gl.disable(gl.STENCIL_TEST);
- assert.equal(myp5._renderer._userEnabledStencil, false);
+ gl.disable(gl.STENCIL_TEST);
+ assert.equal(myp5._renderer._userEnabledStencil, false);
- myp5.push();
- myp5.clip(() => {
- myp5.rect(0, 0, 10, 10);
- });
- assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
- myp5.pop();
+ myp5.push();
+ myp5.clip(() => {
+ myp5.rect(0, 0, 10, 10);
+ });
+ assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
+ myp5.pop();
- myp5.redraw();
+ myp5.redraw();
- assert.equal(myp5._renderer._userEnabledStencil, false);
- assert.equal(gl.isEnabled(gl.STENCIL_TEST), false);
- }
- );
+ assert.equal(myp5._renderer._userEnabledStencil, false);
+ assert.equal(gl.isEnabled(gl.STENCIL_TEST), false);
+ });
});
- suite('Matrix getters', function() {
- test('uModelMatrix', function() {
+ suite('Matrix getters', function () {
+ test('uModelMatrix', function () {
p5.registerAddon(function (p5, fn) {
- fn.checkModelMatrix = function() {
+ fn.checkModelMatrix = function () {
assert.deepEqual(
[...this._renderer.uModelMatrix.mat4],
- [
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 5, 0, 0, 1
- ]
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5, 0, 0, 1]
);
};
});
@@ -3122,17 +3118,12 @@ void main() {
myp5.checkModelMatrix();
});
- test('uViewMatrix', function() {
+ test('uViewMatrix', function () {
p5.registerAddon(function (p5, fn) {
- fn.checkViewMatrix = function() {
+ fn.checkViewMatrix = function () {
assert.deepEqual(
[...this._renderer.uViewMatrix.mat4],
- [
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, -500, 1
- ]
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -500, 1]
);
};
});
@@ -3142,17 +3133,12 @@ void main() {
myp5.checkViewMatrix();
});
- test('uMVMatrix', function() {
+ test('uMVMatrix', function () {
p5.registerAddon(function (p5, fn) {
- fn.checkMVMatrix = function() {
+ fn.checkMVMatrix = function () {
assert.deepEqual(
[...this._renderer.uMVMatrix.mat4],
- [
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 5, 0, -500, 1
- ]
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5, 0, -500, 1]
);
};
});
@@ -3162,16 +3148,14 @@ void main() {
myp5.checkMVMatrix();
});
- test('uPMatrix', function() {
+ test('uPMatrix', function () {
p5.registerAddon(function (p5, fn) {
- fn.checkPMatrix = function() {
+ fn.checkPMatrix = function () {
assert.deepEqual(
[...this._renderer.uPMatrix.mat4],
[
- 32, 0, 0, 0,
- 0, -32, 0, 0,
- 0, 0, -1.0202020406723022, -1,
- 0, 0, -161.6161651611328, 0
+ 32, 0, 0, 0, 0, -32, 0, 0, 0, 0, -1.0202020406723022, -1, 0, 0,
+ -161.6161651611328, 0
]
);
};
@@ -3181,8 +3165,8 @@ void main() {
});
});
- suite('buildGeometry', function() {
- test('captures geometry without drawing', function() {
+ suite('buildGeometry', function () {
+ test('captures geometry without drawing', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.background(255, 0, 0);
const geom = myp5.buildGeometry(() => myp5.circle(0, 0, 5));
@@ -3190,7 +3174,7 @@ void main() {
expect(myp5.get(5, 5)).toEqual([255, 0, 0, 255]);
});
- test('returns fill state back to a previous color', function() {
+ test('returns fill state back to a previous color', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.background(255, 0, 0);
myp5.fill(0, 255, 0);
@@ -3198,7 +3182,7 @@ void main() {
myp5.model(geom);
expect(myp5.get(5, 5)).toEqual([0, 255, 0, 255]);
});
- test('returns fill state back to no fill', function() {
+ test('returns fill state back to no fill', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.background(255, 0, 0);
myp5.noFill();
@@ -3206,7 +3190,7 @@ void main() {
myp5.model(geom);
expect(myp5.get(5, 5)).toEqual([255, 0, 0, 255]);
});
- test('does not throw with a large number of vertices', function() {
+ test('does not throw with a large number of vertices', function () {
myp5.createCanvas(10, 10, myp5.WEBGL);
// Enough triangles to exceed the ~65k argument limit of Function.prototype.apply,
// which would cause a stack overflow if vertices were spread into push() calls.
@@ -3225,8 +3209,8 @@ void main() {
});
});
- suite('fontWidth', function() {
- test('respects textSize changes across push/pop', async function() {
+ suite('fontWidth', function () {
+ test('respects textSize changes across push/pop', async function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
const font = await myp5.loadFont('test/unit/assets/acmesa.ttf');
@@ -3243,7 +3227,7 @@ void main() {
expect(widthAt20).toBeGreaterThan(widthAt12);
});
- test('fontWidth restores correctly when font is unset inside push/pop', async function() {
+ test('fontWidth restores correctly when font is unset inside push/pop', async function () {
myp5.createCanvas(100, 100, myp5.WEBGL);
const font = await myp5.loadFont('test/unit/assets/acmesa.ttf');
myp5.textFont(font);
diff --git a/test/unit/webgl/p5.Shader.js b/test/unit/webgl/p5.Shader.js
index 3869dddd85..db552b92d8 100644
--- a/test/unit/webgl/p5.Shader.js
+++ b/test/unit/webgl/p5.Shader.js
@@ -10,29 +10,31 @@ vi.mock('../../../src/strands/strands_FES', () => {
};
return {
userError,
- internalError: (msg) => { throw new Error(`[p5.strands internal error]: ${msg}`); },
+ internalError: msg => {
+ throw new Error(`[p5.strands internal error]: ${msg}`);
+ },
dimensionMismatchError: (declaredDim, actualDim, varName) => {
userError(
'dimension mismatch',
`Cannot assign a value of dimension ${actualDim} to \`${varName}\`, which expects dimension ${declaredDim}.`
);
- },
+ }
};
});
-suite('p5.Shader', function() {
+suite('p5.Shader', function () {
var myp5;
- beforeAll(function() {
+ beforeAll(function () {
window.IS_MINIFIED = true;
- myp5 = new p5(function(p) {
- p.setup = function() {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, p.WEBGL);
p.pointLight(250, 250, 250, 100, 100, 0);
p.ambientMaterial(250);
};
});
});
- var testUniforms = function(shaderName, uniforms, expectedUniforms) {
+ var testUniforms = function (shaderName, uniforms, expectedUniforms) {
// assert(expectedUniforms.length === Object.keys(uniforms).length,
// shaderName + ' expected ' + expectedUniforms.length + ' uniforms but has ' +
// Object.keys(uniforms).length);
@@ -45,7 +47,7 @@ suite('p5.Shader', function() {
);
}
};
- var testAttributes = function(shaderName, attributes, expectedAttributes) {
+ var testAttributes = function (shaderName, attributes, expectedAttributes) {
// assert(expectedAttributes.length === Object.keys(attributes).length,
// shaderName + ' expected ' + expectedAttributes.length +
// ' attributes but has ' + Object.keys(attributes).length);
@@ -58,7 +60,7 @@ suite('p5.Shader', function() {
);
}
};
- var testShader = function(
+ var testShader = function (
shaderName,
shaderObj,
expectedAttributes,
@@ -69,11 +71,11 @@ suite('p5.Shader', function() {
testUniforms(shaderName, shaderObj.uniforms, expectedUniforms);
shaderObj.unbindShader();
};
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
- suite('Shader', function() {
- test('Light Shader', function() {
+ suite('Shader', function () {
+ test('Light Shader', function () {
var expectedAttributes = ['aPosition', 'aNormal', 'aTexCoord'];
var expectedUniforms = [
'uModelViewMatrix',
@@ -111,7 +113,7 @@ suite('p5.Shader', function() {
expectedUniforms
);
});
- test('Color Shader definition', function() {
+ test('Color Shader definition', function () {
var expectedAttributes = ['aPosition'];
var expectedUniforms = [
'uModelViewMatrix',
@@ -125,12 +127,9 @@ suite('p5.Shader', function() {
expectedUniforms
);
});
- test('Immediate Mode Shader definition', function() {
+ test('Immediate Mode Shader definition', function () {
var expectedAttributes = ['aPosition', 'aVertexColor'];
- var expectedUniforms = [
- 'uModelViewMatrix',
- 'uProjectionMatrix'
- ];
+ var expectedUniforms = ['uModelViewMatrix', 'uProjectionMatrix'];
testShader(
'Immediate Mode Shader',
myp5._renderer._getColorShader(),
@@ -138,7 +137,7 @@ suite('p5.Shader', function() {
expectedUniforms
);
});
- test('Normal Shader definition', function() {
+ test('Normal Shader definition', function () {
var expectedAttributes = ['aPosition', 'aNormal'];
var expectedUniforms = [
'uModelViewMatrix',
@@ -152,7 +151,7 @@ suite('p5.Shader', function() {
expectedUniforms
);
});
- test('Color Shader is set after fill()', function() {
+ test('Color Shader is set after fill()', function () {
myp5.fill(0);
var retainedColorShader = myp5._renderer._getColorShader();
var texLightShader = myp5._renderer._getLightShader();
@@ -172,7 +171,7 @@ suite('p5.Shader', function() {
"_renderer's immediate mode shader was not color shader after fill"
);
});
- test('Normal Shader is set after normalMaterial()', function() {
+ test('Normal Shader is set after normalMaterial()', function () {
myp5.normalMaterial();
var normalShader = myp5._renderer._getNormalShader();
var selectedRetainedShader = myp5._renderer._getFillShader();
@@ -186,7 +185,7 @@ suite('p5.Shader', function() {
"_renderer's retain mode shader was not normal shader"
);
});
- test('Light shader set after ambientMaterial()', function() {
+ test('Light shader set after ambientMaterial()', function () {
var lightShader = myp5._renderer._getLightShader();
myp5.ambientMaterial(128);
var selectedRetainedShader = myp5._renderer._getFillShader();
@@ -202,7 +201,7 @@ suite('p5.Shader', function() {
'after call to ambientMaterial()'
);
});
- test('Light shader set after specularMaterial()', function() {
+ test('Light shader set after specularMaterial()', function () {
var lightShader = myp5._renderer._getLightShader();
myp5.specularMaterial(128);
var selectedRetainedShader = myp5._renderer._getFillShader();
@@ -218,7 +217,7 @@ suite('p5.Shader', function() {
'after call to specularMaterial()'
);
});
- test('Light shader set after emissiveMaterial()', function() {
+ test('Light shader set after emissiveMaterial()', function () {
var lightShader = myp5._renderer._getLightShader();
myp5.emissiveMaterial(128);
var selectedRetainedShader = myp5._renderer._getFillShader();
@@ -234,18 +233,18 @@ suite('p5.Shader', function() {
'after call to emissiveMaterial()'
);
});
- test('Able to setUniform empty arrays', function() {
+ test('Able to setUniform empty arrays', function () {
myp5.shader(myp5._renderer._getLightShader());
var s = myp5._renderer.states.userFillShader;
s.setUniform('uMaterialColor', []);
s.setUniform('uLightingDirection', []);
});
- test('Able to set shininess', function() {
+ test('Able to set shininess', function () {
assert.deepEqual(myp5._renderer.states._useShininess, 1);
myp5.shininess(50);
assert.deepEqual(myp5._renderer.states._useShininess, 50);
});
- test('Shader is reset after resetShader is called', function() {
+ test('Shader is reset after resetShader is called', function () {
myp5.shader(myp5._renderer._getColorShader());
var prevShader = myp5._renderer.states.userFillShader;
assert.isTrue(prevShader !== null);
@@ -253,7 +252,7 @@ suite('p5.Shader', function() {
var curShader = myp5._renderer.states.userFillShader;
assert.isTrue(curShader === null);
});
- test('version() detects a #version directive', function() {
+ test('version() detects a #version directive', function () {
const shader = myp5.createShader(
`
#version 300 es
@@ -276,9 +275,9 @@ suite('p5.Shader', function() {
);
assert.strictEqual(shader.version(), '300 es');
});
- suite('Hooks', function() {
+ suite('Hooks', function () {
let myShader;
- beforeEach(function() {
+ beforeEach(function () {
myShader = myp5.createShader(
`
precision highp float;
@@ -319,7 +318,7 @@ suite('p5.Shader', function() {
}
);
});
- test('available hooks show up in inspectHooks()', function() {
+ test('available hooks show up in inspectHooks()', function () {
const logs = [];
const myLog = (...data) => logs.push(data.join(', '));
const oldLog = console.log;
@@ -328,24 +327,28 @@ suite('p5.Shader', function() {
console.log = oldLog;
expect(logs.join('\n')).to.match(/vec4 getVertexColor/);
});
- test('unfilled hooks do not have an AUGMENTED_HOOK define', function() {
+ test('unfilled hooks do not have an AUGMENTED_HOOK define', function () {
const modified = myShader.modify({});
- expect(modified.fragSrc()).not.to.match(/#define AUGMENTED_HOOK_getVertexColor/);
+ expect(modified.fragSrc()).not.to.match(
+ /#define AUGMENTED_HOOK_getVertexColor/
+ );
});
- test('anonymous function shaderModifier does not throw when parsed', function() {
- const callModify = () => myShader.modify(function() {});
+ test('anonymous function shaderModifier does not throw when parsed', function () {
+ const callModify = () => myShader.modify(function () {});
expect(callModify).not.toThrowError();
});
- test('filled hooks do have an AUGMENTED_HOOK define', function() {
+ test('filled hooks do have an AUGMENTED_HOOK define', function () {
const modified = myShader.modify({
'vec4 getVertexColor': `(vec4 c) {
return vec4(1., 0., 0., 1.);
}`
});
- expect(modified.fragSrc()).to.match(/#define AUGMENTED_HOOK_getVertexColor/);
+ expect(modified.fragSrc()).to.match(
+ /#define AUGMENTED_HOOK_getVertexColor/
+ );
});
});
- test('framebuffer textures are unbound when you draw to the framebuffer', function() {
+ test('framebuffer textures are unbound when you draw to the framebuffer', function () {
const sh = myp5.baseMaterialShader().modify({
uniforms: {
'sampler2D myTex': null
@@ -364,8 +367,8 @@ suite('p5.Shader', function() {
assert.deepEqual(myp5.get(0, 0), [255, 0, 0, 255]);
});
});
- suite('hookTypes', function() {
- test('Produces expected types on baseFilterShader()', function() {
+ suite('hookTypes', function () {
+ test('Produces expected types on baseFilterShader()', function () {
const types = myp5.baseFilterShader().hookTypes('vec4 getColor');
assert.deepEqual(types, {
name: 'getColor',
@@ -456,12 +459,7 @@ suite('p5.Shader', function() {
myp5.createCanvas(5, 5, myp5.WEBGL);
function myMaterial() {
myp5.getPixelInputs(inputs => {
- inputs.color = [
- 1,
- 0,
- 0,
- 1
- ];
+ inputs.color = [1, 0, 0, 1];
return inputs;
});
}
@@ -474,17 +472,16 @@ suite('p5.Shader', function() {
test('does not break when arrays are in uniform callbacks', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
- const myShader = myp5.baseMaterialShader().modify(() => {
- const size = myp5.uniformVector2(() => [myp5.width, myp5.height]);
- myp5.getPixelInputs(inputs => {
- inputs.color = [
- size / 1000,
- 0,
- 1
- ];
- return inputs;
- });
- }, { myp5 });
+ const myShader = myp5.baseMaterialShader().modify(
+ () => {
+ const size = myp5.uniformVector2(() => [myp5.width, myp5.height]);
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [size / 1000, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
expect(() => {
myp5.shader(myShader);
myp5.plane(myp5.width, myp5.height);
@@ -494,13 +491,16 @@ suite('p5.Shader', function() {
test('buildFilterShader can use numeric constants from scope', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
const constants = { val: 100 };
- const myShader = myp5.buildFilterShader(({ constants }) => {
- filterColor.begin();
- let c = 0;
- c += constants.val / 255;
- filterColor.set([c, c, c, 1]);
- filterColor.end();
- }, { constants });
+ const myShader = myp5.buildFilterShader(
+ ({ constants }) => {
+ filterColor.begin();
+ let c = 0;
+ c += constants.val / 255;
+ filterColor.set([c, c, c, 1]);
+ filterColor.end();
+ },
+ { constants }
+ );
expect(() => {
myp5.filter(myShader);
}).not.toThrowError();
@@ -509,67 +509,82 @@ suite('p5.Shader', function() {
test('buildMaterialShader forwards scope to modify', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
expect(() => {
- const myShader = myp5.buildMaterialShader(() => {
- myp5.getPixelInputs(inputs => {
- inputs.color = [1, 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ const myShader = myp5.buildMaterialShader(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [1, 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(myShader);
myp5.plane(myp5.width, myp5.height);
}).not.toThrowError();
});
-test('returns numbers for builtin globals outside hooks and a strandNode when called inside hooks', () => {
- myp5.createCanvas(5, 5, myp5.WEBGL);
- myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const mxInHook = myp5.mouseX;
- const wInHook = myp5.width;
- assert.isTrue(mxInHook.isStrandsNode);
- assert.isTrue(wInHook.isStrandsNode);
- inputs.color = [1, 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
-
- const mx = myp5.mouseX;
- const w = myp5.width;
- assert.isNumber(mx);
- assert.isNumber(w);
- assert.strictEqual(w, myp5.width);
-});
+ test('returns numbers for builtin globals outside hooks and a strandNode when called inside hooks', () => {
+ myp5.createCanvas(5, 5, myp5.WEBGL);
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const mxInHook = myp5.mouseX;
+ const wInHook = myp5.width;
+ assert.isTrue(mxInHook.isStrandsNode);
+ assert.isTrue(wInHook.isStrandsNode);
+ inputs.color = [1, 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
+
+ const mx = myp5.mouseX;
+ const w = myp5.width;
+ assert.isNumber(mx);
+ assert.isNumber(w);
+ assert.strictEqual(w, myp5.width);
+ });
test('builtin global accessors remain usable across modify calls and after failures', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
- myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- inputs.color = [myp5.mouseX / myp5.width, 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [myp5.mouseX / myp5.width, 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- throw new Error('intentional failure');
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ throw new Error('intentional failure');
+ },
+ { myp5 }
+ );
}).toThrowError('intentional failure');
- myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- // global accessors are still usable
- // meaning _builtinGlobalsAccessorsInstalled is not accidentally reset
- const mxInHook = myp5.mouseX;
- const wInHook = myp5.width;
- // inside modify hooks, global accessors return strands nodes
- assert.isTrue(mxInHook.isStrandsNode);
- assert.isTrue(wInHook.isStrandsNode);
- inputs.color = [1, 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ // global accessors are still usable
+ // meaning _builtinGlobalsAccessorsInstalled is not accidentally reset
+ const mxInHook = myp5.mouseX;
+ const wInHook = myp5.width;
+ // inside modify hooks, global accessors return strands nodes
+ assert.isTrue(mxInHook.isStrandsNode);
+ assert.isTrue(wInHook.isStrandsNode);
+ inputs.color = [1, 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
// after modify finishes, global accessors go back to ordinary p5 values
assert.isNumber(myp5.mouseX);
@@ -578,29 +593,35 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('instanceIndex is a value and instanceID() is a compatibility alias', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
- myp5.baseMaterialShader().modify(() => {
- myp5.getWorldInputs(inputs => {
- // instanceIndex is a property — no parentheses
- const idx = myp5.instanceIndex;
- assert.isTrue(idx.isStrandsNode);
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getWorldInputs(inputs => {
+ // instanceIndex is a property — no parentheses
+ const idx = myp5.instanceIndex;
+ assert.isTrue(idx.isStrandsNode);
- // instanceID() is a function kept for compatibility
- const idxCompat = myp5.instanceID();
- assert.isTrue(idxCompat.isStrandsNode);
+ // instanceID() is a function kept for compatibility
+ const idxCompat = myp5.instanceID();
+ assert.isTrue(idxCompat.isStrandsNode);
- return inputs;
- });
- }, { myp5 });
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
});
test('map() works inside a strands modify callback', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const v = myp5.map(0.25, 0, 1, 0, 1);
- inputs.color = [v, v, v, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const v = myp5.map(0.25, 0, 1, 0, 1);
+ inputs.color = [v, v, v, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -614,13 +635,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('map() with withinBounds clamps the result inside strands', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const v = myp5.map(2.0, 0, 1, 0, 1, true);
- inputs.color = [v, v, v, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const v = myp5.map(2.0, 0, 1, 0, 1, true);
+ inputs.color = [v, v, v, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -634,13 +658,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('map() shrinks a wider input range into a narrower output range', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const v = myp5.map(5, 0, 10, 0, 1);
- inputs.color = [v, v, v, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const v = myp5.map(5, 0, 10, 0, 1);
+ inputs.color = [v, v, v, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -654,13 +681,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('map() handles offset output ranges correctly', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const v = myp5.map(0.5, 0, 1, 0.2, 0.8);
- inputs.color = [v, v, v, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const v = myp5.map(0.5, 0, 1, 0.2, 0.8);
+ inputs.color = [v, v, v, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -674,13 +704,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('map() handles a negative input range', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const v = myp5.map(0, -1, 1, 0, 1);
- inputs.color = [v, v, v, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const v = myp5.map(0, -1, 1, 0, 1);
+ inputs.color = [v, v, v, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -694,13 +727,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('map() remaps texCoord.x into a horizontal gradient', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const v = myp5.map(inputs.texCoord.x, 0, 1, 0.2, 0.8);
- inputs.color = [v, v, v, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const v = myp5.map(inputs.texCoord.x, 0, 1, 0.2, 0.8);
+ inputs.color = [v, v, v, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -715,13 +751,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('color() with hex string returns correct vec4 in strands', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const c = myp5.color('#ff0000');
- inputs.color = [c.x, c.y, c.z, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const c = myp5.color('#ff0000');
+ inputs.color = [c.x, c.y, c.z, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -735,13 +774,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('color() with CSS named color returns correct vec4 in strands', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const c = myp5.color('blue');
- inputs.color = [c.x, c.y, c.z, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const c = myp5.color('blue');
+ inputs.color = [c.x, c.y, c.z, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -755,15 +797,18 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('lerpColor() interpolates between two colors in strands', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const c1 = myp5.color('#ff0000');
- const c2 = myp5.color('#0000ff');
- const mixed = myp5.lerpColor(c1, c2, 0.5);
- inputs.color = [mixed.x, mixed.y, mixed.z, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const c1 = myp5.color('#ff0000');
+ const c2 = myp5.color('#0000ff');
+ const mixed = myp5.lerpColor(c1, c2, 0.5);
+ inputs.color = [mixed.x, mixed.y, mixed.z, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -777,16 +822,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('red(), green(), blue(), alpha() extract correct channels in strands', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const c = myp5.color('#ff8000');
- const r = myp5.red(c);
- const g = myp5.green(c);
- const b = myp5.blue(c);
- inputs.color = [r, g, b, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const c = myp5.color('#ff8000');
+ const r = myp5.red(c);
+ const g = myp5.green(c);
+ const b = myp5.blue(c);
+ inputs.color = [r, g, b, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -800,15 +848,18 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('hue() returns normalized hue value in strands', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- // Pure red has hue 0
- const c = myp5.color('#ff0000');
- const h = myp5.hue(c);
- inputs.color = [h, h, h, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ // Pure red has hue 0
+ const c = myp5.color('#ff0000');
+ const h = myp5.hue(c);
+ inputs.color = [h, h, h, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -820,14 +871,17 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle custom uniform names with automatic values', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // Variable name is 'brightness' but uniform name is 'customBrightness'
- const brightness = myp5.uniformFloat('customBrightness', () => 0.8);
- myp5.getPixelInputs(inputs => {
- inputs.color = [brightness, brightness, brightness, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // Variable name is 'brightness' but uniform name is 'customBrightness'
+ const brightness = myp5.uniformFloat('customBrightness', () => 0.8);
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [brightness, brightness, brightness, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -842,14 +896,17 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle custom uniform names with manual setUniform', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // Variable name is 'brightness' but uniform name is 'customBrightness'
- const brightness = myp5.uniformFloat('customBrightness');
- myp5.getPixelInputs(inputs => {
- inputs.color = [brightness, brightness, brightness, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // Variable name is 'brightness' but uniform name is 'customBrightness'
+ const brightness = myp5.uniformFloat('customBrightness');
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [brightness, brightness, brightness, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
// Set the uniform using the custom name
testShader.setUniform('customBrightness', 0.6);
@@ -867,16 +924,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('renames only problematic inferred uniform names', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // Names containing two underscores are treated as problematic because of GLSL
- // naming constraints, so p5.strands remaps them through a shared naming layer.
- const __val = myp5.uniformFloat(() => 0.8);
- const brightness = myp5.uniformFloat(() => 0.2);
- myp5.getPixelInputs(inputs => {
- inputs.color = [__val, brightness, brightness, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // Names containing two underscores are treated as problematic because of GLSL
+ // naming constraints, so p5.strands remaps them through a shared naming layer.
+ const __val = myp5.uniformFloat(() => 0.8);
+ const brightness = myp5.uniformFloat(() => 0.2);
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [__val, brightness, brightness, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
assert.strictEqual(
testShader.hooks.shaderNameMap.externalToInternal.__val,
@@ -904,14 +964,17 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handles implicit uniform names containing two underscores', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using implicit uniform name with two underscores, which should be remapped
- const __val = myp5.uniformFloat();
- myp5.getPixelInputs(inputs => {
- inputs.color = [__val, __val, __val, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using implicit uniform name with two underscores, which should be remapped
+ const __val = myp5.uniformFloat();
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [__val, __val, __val, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
// setting the uniform using the original name
testShader.setUniform('__val', 0.6);
@@ -929,14 +992,17 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handles explicit uniform names containing two underscores', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using explicit uniform name with two underscores, which should be remapped
- const val = myp5.uniformFloat('__val');
- myp5.getPixelInputs(inputs => {
- inputs.color = [val, val, val, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using explicit uniform name with two underscores, which should be remapped
+ const val = myp5.uniformFloat('__val');
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [val, val, val, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
// setting the uniform using the original name
testShader.setUniform('__val', 0.6);
@@ -953,21 +1019,27 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('continues inferred uniform suffixes across chained modify calls', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const shader1 = myp5.baseMaterialShader().modify(() => {
- const __first = myp5.uniformFloat(() => 0.2);
- myp5.getPixelInputs(inputs => {
- inputs.color.r = __first;
- return inputs;
- });
- }, { myp5 });
+ const shader1 = myp5.baseMaterialShader().modify(
+ () => {
+ const __first = myp5.uniformFloat(() => 0.2);
+ myp5.getPixelInputs(inputs => {
+ inputs.color.r = __first;
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
- const shader2 = shader1.modify(() => {
- const __second = myp5.uniformFloat(() => 0.8);
- myp5.getWorldInputs(inputs => {
- inputs.position.y += __second * 0.0;
- return inputs;
- });
- }, { myp5 });
+ const shader2 = shader1.modify(
+ () => {
+ const __second = myp5.uniformFloat(() => 0.8);
+ myp5.getWorldInputs(inputs => {
+ inputs.position.y += __second * 0.0;
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
assert.strictEqual(
shader2.hooks.shaderNameMap.externalToInternal.__first,
@@ -977,56 +1049,58 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
shader2.hooks.shaderNameMap.externalToInternal.__second,
'_p5_strands_1'
);
- assert.strictEqual(
- shader2.hooks.shaderNameState.nextSuffix,
- 2
- );
+ assert.strictEqual(shader2.hooks.shaderNameState.nextSuffix, 2);
});
test('does not leak shared/varying state into later modify calls', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const firstShader = myp5.baseMaterialShader().modify(() => {
- let __worldPos = myp5.varyingVec3();
- myp5.getWorldInputs(inputs => {
- __worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(__worldPos / 25), 1];
- });
- }, { myp5 });
+ const firstShader = myp5.baseMaterialShader().modify(
+ () => {
+ let __worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ __worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(__worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
const internalName =
firstShader.hooks.shaderNameMap.externalToInternal.__worldPos;
- assert.deepEqual(
- firstShader.hooks.varyingVariables,
- [`vec3 ${internalName}`]
- );
-
- const secondShader = myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- myp5.filterColor.set([1, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ assert.deepEqual(firstShader.hooks.varyingVariables, [
+ `vec3 ${internalName}`
+ ]);
- assert.deepEqual(
- secondShader.hooks.varyingVariables,
- []
+ const secondShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ myp5.filterColor.set([1, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
);
+
+ assert.deepEqual(secondShader.hooks.varyingVariables, []);
assert.notInclude(secondShader.vertSrc(), '__worldPos');
assert.notInclude(secondShader.fragSrc(), '__worldPos');
});
test('rejects explicit use of the reserved internal strands prefix', () => {
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- const brightness = myp5.uniformFloat('_p5_strands_forbidden');
- myp5.getPixelInputs(inputs => {
- inputs.color = [brightness, brightness, brightness, 1.0];
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ const brightness = myp5.uniformFloat('_p5_strands_forbidden');
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [brightness, brightness, brightness, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
}).toThrow('reserved internal p5.strands prefix');
});
@@ -1035,13 +1109,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
// First build a valid strands shader so the inferred name '__val' is
// remapped through the normal shaderNameMap path.
- const mappedShader = myp5.baseMaterialShader().modify(() => {
- const __val = myp5.uniformFloat(() => 0.8);
- myp5.getWorldInputs(inputs => {
- inputs.position.x += __val * 0.0;
- return inputs;
- });
- }, { myp5 });
+ const mappedShader = myp5.baseMaterialShader().modify(
+ () => {
+ const __val = myp5.uniformFloat(() => 0.8);
+ myp5.getWorldInputs(inputs => {
+ inputs.position.x += __val * 0.0;
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
const internalName =
mappedShader.hooks.shaderNameMap.externalToInternal.__val;
@@ -1070,32 +1147,38 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('indexing into array returned from helper function works in WebGL', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
- const myShader = myp5.baseMaterialShader().modify(() => {
- const brightness = myp5.uniformFloat();
- function getArr() {
- return [1, 2];
- }
- const arr = getArr();
- myp5.getPixelInputs(inputs => {
- inputs.color = [arr[0] * brightness, arr[1], 0, 1];
- return inputs;
- });
- }, { myp5 });
- expect(() => {
- myp5.shader(myShader);
- myp5.plane(myp5.width, myp5.height);
+ const myShader = myp5.baseMaterialShader().modify(
+ () => {
+ const brightness = myp5.uniformFloat();
+ function getArr() {
+ return [1, 2];
+ }
+ const arr = getArr();
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [arr[0] * brightness, arr[1], 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
+ expect(() => {
+ myp5.shader(myShader);
+ myp5.plane(myp5.width, myp5.height);
}).not.toThrowError();
});
test('inline literal indexing [1, 2][0] works in WebGL', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
- const myShader = myp5.baseMaterialShader().modify(() => {
- const brightness = myp5.uniformFloat();
- myp5.getPixelInputs(inputs => {
- inputs.color = [[1, 2][0] * brightness, 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ const myShader = myp5.baseMaterialShader().modify(
+ () => {
+ const brightness = myp5.uniformFloat();
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [[1, 2][0] * brightness, 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
expect(() => {
myp5.shader(myShader);
myp5.plane(myp5.width, myp5.height);
@@ -1105,59 +1188,74 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('array literal with 1 element throws descriptive error in WebGL', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- const arr = [1];
- myp5.getPixelInputs(inputs => {
- inputs.color = [arr[0], 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ const arr = [1];
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [arr[0], 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
}).toThrowError('and must have 2-4 elements (got 1)');
});
test('array literal with 5 elements throws descriptive error in WebGL', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- const arr = [1, 2, 3, 4, 5];
- myp5.getPixelInputs(inputs => {
- inputs.color = [arr[0], 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ const arr = [1, 2, 3, 4, 5];
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [arr[0], 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
}).toThrowError('and must have 2-4 elements (got 5)');
});
test('valid array lengths 2, 3, 4 work in WebGL', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
expect(() => {
- const s2 = myp5.baseMaterialShader().modify(() => {
- const arr = [1, 2];
- myp5.getPixelInputs(inputs => {
- inputs.color = [arr[0], 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ const s2 = myp5.baseMaterialShader().modify(
+ () => {
+ const arr = [1, 2];
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [arr[0], 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.shader(s2);
myp5.plane(myp5.width, myp5.height);
- const s3 = myp5.baseMaterialShader().modify(() => {
- const arr = [1, 2, 3];
- myp5.getPixelInputs(inputs => {
- inputs.color = [arr[0], 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ const s3 = myp5.baseMaterialShader().modify(
+ () => {
+ const arr = [1, 2, 3];
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [arr[0], 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.shader(s3);
myp5.plane(myp5.width, myp5.height);
- const s4 = myp5.baseMaterialShader().modify(() => {
- const arr = [1, 2, 3, 4];
- myp5.getPixelInputs(inputs => {
- inputs.color = [arr[0], 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ const s4 = myp5.baseMaterialShader().modify(
+ () => {
+ const arr = [1, 2, 3, 4];
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [arr[0], 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.shader(s4);
myp5.plane(myp5.width, myp5.height);
}).not.toThrowError();
@@ -1167,17 +1265,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
suite('if statement conditionals', () => {
test('handle simple if statement with true condition', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- if (condition > 0.5) {
- color = myp5.float(1.0); // set to white in if branch
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ if (condition > 0.5) {
+ color = myp5.float(1.0); // set to white in if branch
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1189,17 +1290,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle simple if statement with condition that is not a swizzle', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const x = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- if (x > 0.5) {
- color = myp5.float(1.0); // set to white in if branch
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const x = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ if (x > 0.5) {
+ color = myp5.float(1.0); // set to white in if branch
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1211,17 +1315,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle simple if statement with simpler assignment', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = 1; // initial gray
- if (condition > 0.5) {
- color = 1; // set to white in if branch
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = 1; // initial gray
+ if (condition > 0.5) {
+ color = 1; // set to white in if branch
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1233,17 +1340,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle simple if statement with false condition', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 0.0); // false condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- if (condition > 0.5) {
- color = myp5.float(1.0); // set to white in if branch
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 0.0); // false condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ if (condition > 0.5) {
+ color = myp5.float(1.0); // set to white in if branch
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1255,19 +1365,22 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle if-else statement', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 0.0); // false condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- if (condition > 0.5) {
- color = myp5.float(1.0); // white for true
- } else {
- color = myp5.float(0.0); // black for false
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 0.0); // false condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ if (condition > 0.5) {
+ color = myp5.float(1.0); // white for true
+ } else {
+ color = myp5.float(0.0); // black for false
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1279,21 +1392,24 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle multiple variable assignments in if statement', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let red = myp5.float(0.0);
- let green = myp5.float(0.0);
- let blue = myp5.float(0.0);
- if (condition > 0.5) {
- red = myp5.float(1.0);
- green = myp5.float(0.5);
- blue = myp5.float(0.0);
- }
- inputs.color = [red, green, blue, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let red = myp5.float(0.0);
+ let green = myp5.float(0.0);
+ let blue = myp5.float(0.0);
+ if (condition > 0.5) {
+ red = myp5.float(1.0);
+ green = myp5.float(0.5);
+ blue = myp5.float(0.0);
+ }
+ inputs.color = [red, green, blue, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1301,25 +1417,28 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
const pixelColor = myp5.get(25, 25);
assert.approximately(pixelColor[0], 255, 5); // Red channel should be 255
assert.approximately(pixelColor[1], 127, 5); // Green channel should be ~127
- assert.approximately(pixelColor[2], 0, 5); // Blue channel should be ~0
+ assert.approximately(pixelColor[2], 0, 5); // Blue channel should be ~0
});
test('handle modifications after if statement', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0); // start with black
- if (condition > 0.5) {
- color = myp5.float(1.0); // set to white in if branch
- } else {
- color = myp5.float(0.5); // set to gray in else branch
- }
- // Modify the color after the if statement
- color = color * 0.5; // Should result in 0.5 * 1.0 = 0.5 (gray)
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0); // start with black
+ if (condition > 0.5) {
+ color = myp5.float(1.0); // set to white in if branch
+ } else {
+ color = myp5.float(0.5); // set to gray in else branch
+ }
+ // Modify the color after the if statement
+ color = color * 0.5; // Should result in 0.5 * 1.0 = 0.5 (gray)
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1331,24 +1450,27 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle modifications after if statement in both branches', () => {
myp5.createCanvas(100, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const uv = inputs.texCoord;
- const condition = uv.x > 0.5; // left half false, right half true
- let color = myp5.float(0.0);
- if (condition) {
- color = myp5.float(1.0); // white on right side
- } else {
- color = myp5.float(0.8); // light gray on left side
- }
- // Multiply by 0.5 after the if statement
- color = color * 0.5;
- // Right side: 1.0 * 0.5 = 0.5 (medium gray)
- // Left side: 0.8 * 0.5 = 0.4 (darker gray)
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const uv = inputs.texCoord;
+ const condition = uv.x > 0.5; // left half false, right half true
+ let color = myp5.float(0.0);
+ if (condition) {
+ color = myp5.float(1.0); // white on right side
+ } else {
+ color = myp5.float(0.8); // light gray on left side
+ }
+ // Multiply by 0.5 after the if statement
+ color = color * 0.5;
+ // Right side: 1.0 * 0.5 = 0.5 (medium gray)
+ // Left side: 0.8 * 0.5 = 0.4 (darker gray)
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1361,21 +1483,24 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle if-else-if chains', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const value = myp5.uniformFloat(() => 0.5); // middle value
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
- if (value > 0.8) {
- color = myp5.float(1.0); // white for high values
- } else if (value > 0.3) {
- color = myp5.float(0.5); // gray for medium values
- } else {
- color = myp5.float(0.0); // black for low values
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const value = myp5.uniformFloat(() => 0.5); // middle value
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
+ if (value > 0.8) {
+ color = myp5.float(1.0); // white for high values
+ } else if (value > 0.3) {
+ color = myp5.float(0.5); // gray for medium values
+ } else {
+ color = myp5.float(0.0); // black for low values
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1387,21 +1512,24 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle if-else-if chains in the else branch', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const value = myp5.uniformFloat(() => 0.2); // middle value
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
- if (value > 0.8) {
- color = myp5.float(1.0); // white for high values
- } else if (value > 0.3) {
- color = myp5.float(0.5); // gray for medium values
- } else {
- color = myp5.float(0.0); // black for low values
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const value = myp5.uniformFloat(() => 0.2); // middle value
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
+ if (value > 0.8) {
+ color = myp5.float(1.0); // white for high values
+ } else if (value > 0.3) {
+ color = myp5.float(0.5); // gray for medium values
+ } else {
+ color = myp5.float(0.0); // black for low values
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1413,29 +1541,32 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle conditional assignment in if-else-if chains', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const val = myp5.uniformFloat(() => Math.PI * 8);
- myp5.getPixelInputs(inputs => {
- let shininess = 0
- let color = 0
- if (val > 5) {
- const elevation = myp5.sin(val)
- if (elevation > 0.4) {
- shininess = 0;
- } else if (elevation > 0.25) {
- shininess = 30;
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const val = myp5.uniformFloat(() => Math.PI * 8);
+ myp5.getPixelInputs(inputs => {
+ let shininess = 0;
+ let color = 0;
+ if (val > 5) {
+ const elevation = myp5.sin(val);
+ if (elevation > 0.4) {
+ shininess = 0;
+ } else if (elevation > 0.25) {
+ shininess = 30;
+ } else {
+ color = 1;
+ shininess = 100;
+ }
} else {
- color = 1;
- shininess = 100;
+ shininess += 25;
}
- } else {
- shininess += 25;
- }
- inputs.shininess = shininess;
- inputs.color = [color, color, color, 1];
- return inputs;
- });
- }, { myp5 });
+ inputs.shininess = shininess;
+ inputs.color = [color, color, color, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1447,24 +1578,27 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle nested if statements', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const outerCondition = myp5.uniformFloat(() => 1.0); // true
- const innerCondition = myp5.uniformFloat(() => 1.0); // true
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
- if (outerCondition > 0.5) {
- if (innerCondition > 0.5) {
- color = myp5.float(1.0); // white for both conditions true
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const outerCondition = myp5.uniformFloat(() => 1.0); // true
+ const innerCondition = myp5.uniformFloat(() => 1.0); // true
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
+ if (outerCondition > 0.5) {
+ if (innerCondition > 0.5) {
+ color = myp5.float(1.0); // white for both conditions true
+ } else {
+ color = myp5.float(0.5); // gray for outer true, inner false
+ }
} else {
- color = myp5.float(0.5); // gray for outer true, inner false
+ color = myp5.float(0.0); // black for outer false
}
- } else {
- color = myp5.float(0.0); // black for outer false
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1476,16 +1610,22 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle if statement with || (OR) operator', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let c = [1, 1, 1, 1];
- if (myp5.abs(inputs.texCoord.x - 0.5) > 0.2 || myp5.abs(inputs.texCoord.y - 0.5) > 0.2) {
- c = [1, 0, 0, 1];
- }
- inputs.color = c;
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let c = [1, 1, 1, 1];
+ if (
+ myp5.abs(inputs.texCoord.x - 0.5) > 0.2 ||
+ myp5.abs(inputs.texCoord.y - 0.5) > 0.2
+ ) {
+ c = [1, 0, 0, 1];
+ }
+ inputs.color = c;
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1504,16 +1644,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle if statement with && (AND) operator', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
- if (inputs.texCoord.x > 0.5 && inputs.texCoord.y > 0.5) {
- color = myp5.float(1.0);
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
+ if (inputs.texCoord.x > 0.5 && inputs.texCoord.y > 0.5) {
+ color = myp5.float(1.0);
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1536,16 +1679,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle struct property assignment in if-else branches', () => {
myp5.createCanvas(100, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- if (inputs.texCoord.x > 0.5) {
- inputs.color = [1, 0, 0, 1];
- } else {
- inputs.color = [0, 0, 1, 1];
- }
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ if (inputs.texCoord.x > 0.5) {
+ inputs.color = [1, 0, 0, 1];
+ } else {
+ inputs.color = [0, 0, 1, 1];
+ }
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1564,25 +1710,27 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
// Keep one direct API test for completeness
test('handle direct StrandsIf API usage', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const conditionValue = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- const assignments = myp5.strandsIf(
- conditionValue.greaterThan(0),
- () => {
- let tmp = color.copy();
- tmp = myp5.float(1.0); // set to white in if branch
- return { color: tmp };
- }
- ).Else(() => {
- return { color: color }; // keep original in else branch
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const conditionValue = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ const assignments = myp5
+ .strandsIf(conditionValue.greaterThan(0), () => {
+ let tmp = color.copy();
+ tmp = myp5.float(1.0); // set to white in if branch
+ return { color: tmp };
+ })
+ .Else(() => {
+ return { color: color }; // keep original in else branch
+ });
+ color = assignments.color;
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
});
- color = assignments.color;
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1594,34 +1742,34 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle direct StrandsIf ElseIf API usage', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const value = myp5.uniformFloat(() => 0.5); // middle value
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0); // initial black
- const assignments = myp5.strandsIf(
- value.greaterThan(0.8),
- () => {
- let tmp = color.copy();
- tmp = myp5.float(1.0); // white for high values
- return { color: tmp };
- }
- ).ElseIf(
- value.greaterThan(0.3),
- () => {
- let tmp = color.copy();
- tmp = myp5.float(0.5); // gray for medium values
- return { color: tmp };
- }
- ).Else(() => {
- let tmp = color.copy();
- tmp = myp5.float(0.0); // black for low values
- return { color: tmp };
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const value = myp5.uniformFloat(() => 0.5); // middle value
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0); // initial black
+ const assignments = myp5
+ .strandsIf(value.greaterThan(0.8), () => {
+ let tmp = color.copy();
+ tmp = myp5.float(1.0); // white for high values
+ return { color: tmp };
+ })
+ .ElseIf(value.greaterThan(0.3), () => {
+ let tmp = color.copy();
+ tmp = myp5.float(0.5); // gray for medium values
+ return { color: tmp };
+ })
+ .Else(() => {
+ let tmp = color.copy();
+ tmp = myp5.float(0.0); // black for low values
+ return { color: tmp };
+ });
+ color = assignments.color;
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
});
- color = assignments.color;
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1634,24 +1782,31 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle comma-separated expressions with assignments', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let red = myp5.float(0.0);
- let green = myp5.float(0.0);
- let blue = myp5.float(0.0);
-
- if (condition > 0.5) {
- // Use comma-separated expressions with assignments
- red = myp5.float(1.0), green = myp5.float(0.5), blue = myp5.float(0.2);
- } else {
- red = myp5.float(0.0), green = myp5.float(0.0), blue = myp5.float(0.0);
- }
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let red = myp5.float(0.0);
+ let green = myp5.float(0.0);
+ let blue = myp5.float(0.0);
+
+ if (condition > 0.5) {
+ // Use comma-separated expressions with assignments
+ ((red = myp5.float(1.0)),
+ (green = myp5.float(0.5)),
+ (blue = myp5.float(0.2)));
+ } else {
+ ((red = myp5.float(0.0)),
+ (green = myp5.float(0.0)),
+ (blue = myp5.float(0.0)));
+ }
- inputs.color = [red, green, blue, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [red, green, blue, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1666,16 +1821,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle early return in if-else branches', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- let value = 1;
- if (value > 0.5) {
- return [1, 0, 0, 1];
- } else {
- return [0, 1, 0, 1];
- }
- });
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ let value = 1;
+ if (value > 0.5) {
+ return [1, 0, 0, 1];
+ } else {
+ return [0, 1, 0, 1];
+ }
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -1689,18 +1847,21 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle early return in if with content afterwards', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- let value = 1;
- if (value > 0.5) {
- return [1, 0, 0, 1];
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ let value = 1;
+ if (value > 0.5) {
+ return [1, 0, 0, 1];
+ }
- let otherValue = 0.2;
- otherValue *= 2;
- return [otherValue, 0, 0, 1];
- });
- }, { myp5 });
+ let otherValue = 0.2;
+ otherValue *= 2;
+ return [otherValue, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -1714,18 +1875,21 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle false early return in if with content afterwards', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- let value = 1;
- if (value < 0.5) {
- return [1, 0, 0, 1];
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ let value = 1;
+ if (value < 0.5) {
+ return [1, 0, 0, 1];
+ }
- let otherValue = 0.2;
- otherValue *= 2;
- return [otherValue, 0, 0, 1];
- });
- }, { myp5 });
+ let otherValue = 0.2;
+ otherValue *= 2;
+ return [otherValue, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -1739,22 +1903,25 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('using boolean intermediate variables', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- let value = 1;
- let condition = 1 > 2;
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ let value = 1;
+ let condition = 1 > 2;
- if (value < 0.5) {
- condition = 0.5 < 2;
- }
+ if (value < 0.5) {
+ condition = 0.5 < 2;
+ }
- if (condition) {
- return [1, 0, 0, 1]
- }
+ if (condition) {
+ return [1, 0, 0, 1];
+ }
- return [0.4, 0, 0, 1];
- });
- }, { myp5 });
+ return [0.4, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -1768,23 +1935,26 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('using boolean intermediate variables in functions', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- const conditionMet = () => {
- let condition = 1 > 2;
- let value = 1;
- if (value < 0.5) {
- condition = 0.5 < 2;
- }
- return !condition
- }
- myp5.getColor((inputs, canvasContent) => {
- if (conditionMet()) {
- return [1, 0, 0, 1]
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ const conditionMet = () => {
+ let condition = 1 > 2;
+ let value = 1;
+ if (value < 0.5) {
+ condition = 0.5 < 2;
+ }
+ return !condition;
+ };
+ myp5.getColor((inputs, canvasContent) => {
+ if (conditionMet()) {
+ return [1, 0, 0, 1];
+ }
- return [0.4, 0, 0, 1];
- });
- }, { myp5 });
+ return [0.4, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -1798,23 +1968,26 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('using boolean intermediate variables in functions with early returns', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- const conditionMet = () => {
- let value = 1;
- if (value < 0.5) {
- return true
- }
- return false
- }
- myp5.getColor((inputs, canvasContent) => {
- if (conditionMet()) {
- return [1, 0, 0, 1]
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ const conditionMet = () => {
+ let value = 1;
+ if (value < 0.5) {
+ return true;
+ }
+ return false;
+ };
+ myp5.getColor((inputs, canvasContent) => {
+ if (conditionMet()) {
+ return [1, 0, 0, 1];
+ }
- return [0.4, 0, 0, 1];
- });
- }, { myp5 });
- console.log(testShader.fragSrc())
+ return [0.4, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
+ console.log(testShader.fragSrc());
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -1829,12 +2002,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
suite('ternary expressions', () => {
test('ternary changes color based on left/right side of canvas', () => {
myp5.createCanvas(50, 25, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- inputs.color = inputs.texCoord.x > 0.5 ? [1, 0, 0, 1] : [0, 0, 1, 1];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ inputs.color =
+ inputs.texCoord.x > 0.5 ? [1, 0, 0, 1] : [0, 0, 1, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1852,13 +2029,16 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('ternary with scalar values', () => {
myp5.createCanvas(50, 25, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const brightness = inputs.texCoord.x > 0.5 ? 1.0 : 0.0;
- inputs.color = [brightness, brightness, brightness, 1];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const brightness = inputs.texCoord.x > 0.5 ? 1.0 : 0.0;
+ inputs.color = [brightness, brightness, brightness, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -1879,18 +2059,21 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle simple for loop with known iteration count', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
- for (let i = 0; i < 3; i++) {
- color = color + 0.1;
- }
+ for (let i = 0; i < 3; i++) {
+ color = color + 0.1;
+ }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -1906,18 +2089,21 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle swizzle assignments in loops', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let color = [0, 0, 0, 1];
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let color = [0, 0, 0, 1];
- for (let i = 0; i < 3; i++) {
- color.rgb += 0.1;
- }
+ for (let i = 0; i < 3; i++) {
+ color.rgb += 0.1;
+ }
- inputs.color = color;
- return inputs;
- });
- }, { myp5 });
+ inputs.color = color;
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -1933,20 +2119,23 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle for loop with variable as loop bound', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const maxIterations = myp5.uniformInt(() => 2);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const maxIterations = myp5.uniformInt(() => 2);
- myp5.getPixelInputs(inputs => {
- let result = myp5.float(0.0);
+ myp5.getPixelInputs(inputs => {
+ let result = myp5.float(0.0);
- for (let i = 0; i < maxIterations; i++) {
- result = result + 0.25;
- }
+ for (let i = 0; i < maxIterations; i++) {
+ result = result + 0.25;
+ }
- inputs.color = [result, result, result, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [result, result, result, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -1962,20 +2151,23 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle for loop modifying multiple variables', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let red = myp5.float(0.0);
- let green = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let red = myp5.float(0.0);
+ let green = myp5.float(0.0);
- for (let i = 0; i < 4; i++) {
- red = red + 0.125; // 4 * 0.125 = 0.5
- green = green + 0.25; // 4 * 0.25 = 1.0
- }
+ for (let i = 0; i < 4; i++) {
+ red = red + 0.125; // 4 * 0.125 = 0.5
+ green = green + 0.25; // 4 * 0.25 = 1.0
+ }
- inputs.color = [red, green, 0.0, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [red, green, 0.0, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -1984,27 +2176,32 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
const pixelColor = myp5.get(25, 25);
assert.approximately(pixelColor[0], 127, 5); // 0.5 * 255 ≈ 127
assert.approximately(pixelColor[1], 255, 5); // 1.0 * 255 = 255
- assert.approximately(pixelColor[2], 0, 5); // 0.0 * 255 = 0
+ assert.approximately(pixelColor[2], 0, 5); // 0.0 * 255 = 0
});
test('handle for loop modifying multiple variables after minification', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let red = myp5.float(0.0);
- let green = myp5.float(0.0);
-
- for (let i = 0; i < 4; i++) {
- // Note the comma!
- red = red + 0.125, // 4 * 0.125 = 0.5
- green = green + 0.25; // 4 * 0.25 = 1.0
- }
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let red = myp5.float(0.0);
+ let green = myp5.float(0.0);
+
+ for (let i = 0; i < 4; i++) {
+ // Note the comma!
+ ((red =
+ red +
+ 0.125), // 4 * 0.125 = 0.5
+ (green = green + 0.25)); // 4 * 0.25 = 1.0
+ }
- inputs.color = [red, green, 0.0, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [red, green, 0.0, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2013,26 +2210,29 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
const pixelColor = myp5.get(25, 25);
assert.approximately(pixelColor[0], 127, 5); // 0.5 * 255 ≈ 127
assert.approximately(pixelColor[1], 255, 5); // 1.0 * 255 = 255
- assert.approximately(pixelColor[2], 0, 5); // 0.0 * 255 = 0
+ assert.approximately(pixelColor[2], 0, 5); // 0.0 * 255 = 0
});
test('handle for loop with conditional inside', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let sum = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let sum = myp5.float(0.0);
- for (let i = 0; i < 5; i++) {
- if (i % 2 === 0) {
- sum = sum + 0.1; // Add on even iterations: 0, 2, 4
+ for (let i = 0; i < 5; i++) {
+ if (i % 2 === 0) {
+ sum = sum + 0.1; // Add on even iterations: 0, 2, 4
+ }
}
- }
- inputs.color = [sum, sum, sum, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [sum, sum, sum, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2048,20 +2248,23 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle nested for loops', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let total = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let total = myp5.float(0.0);
- for (let i = 0; i < 2; i++) {
- for (let j = 0; j < 3; j++) {
- total = total + 0.05; // 2 * 3 = 6 iterations
+ for (let i = 0; i < 2; i++) {
+ for (let j = 0; j < 3; j++) {
+ total = total + 0.05; // 2 * 3 = 6 iterations
+ }
}
- }
- inputs.color = [total, total, total, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [total, total, total, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2077,35 +2280,38 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle complex nested for loops with multiple phi assignments', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let outerSum = myp5.float(0.0);
- let globalCounter = myp5.float(0.0);
-
- // Outer for loop modifying multiple variables
- for (let i = 0; i < 2; i++) {
- let innerSum = myp5.float(0.0);
- let localCounter = myp5.float(0.0);
-
- // Inner for loop also modifying multiple variables
- for (let j = 0; j < 2; j++) {
- innerSum = innerSum + 0.1;
- localCounter = localCounter + 1.0;
- globalCounter = globalCounter + 0.5; // This modifies outer scope
- }
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let outerSum = myp5.float(0.0);
+ let globalCounter = myp5.float(0.0);
+
+ // Outer for loop modifying multiple variables
+ for (let i = 0; i < 2; i++) {
+ let innerSum = myp5.float(0.0);
+ let localCounter = myp5.float(0.0);
+
+ // Inner for loop also modifying multiple variables
+ for (let j = 0; j < 2; j++) {
+ innerSum = innerSum + 0.1;
+ localCounter = localCounter + 1.0;
+ globalCounter = globalCounter + 0.5; // This modifies outer scope
+ }
- // Complex state modification between loops involving all variables
- innerSum = innerSum * localCounter; // 0.2 * 2.0 = 0.4
- outerSum = outerSum + innerSum; // Add to outer sum
- globalCounter = globalCounter * 0.5; // Modify global again
- }
+ // Complex state modification between loops involving all variables
+ innerSum = innerSum * localCounter; // 0.2 * 2.0 = 0.4
+ outerSum = outerSum + innerSum; // Add to outer sum
+ globalCounter = globalCounter * 0.5; // Modify global again
+ }
- // Final result should be: 2 iterations * 0.4 = 0.8 for outerSum
- // globalCounter: ((0 + 2*0.5)*0.5 + 2*0.5)*0.5 = ((1)*0.5 + 1)*0.5 = 1.5*0.5 = 0.75
- inputs.color = [outerSum, globalCounter, 0.0, 1.0];
- return inputs;
- });
- }, { myp5 });
+ // Final result should be: 2 iterations * 0.4 = 0.8 for outerSum
+ // globalCounter: ((0 + 2*0.5)*0.5 + 2*0.5)*0.5 = ((1)*0.5 + 1)*0.5 = 1.5*0.5 = 0.75
+ inputs.color = [outerSum, globalCounter, 0.0, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2120,28 +2326,31 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle nested for loops with state modification between loops', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let total = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let total = myp5.float(0.0);
- // Outer for loop
- for (let i = 0; i < 2; i++) {
- let innerSum = myp5.float(0.0);
+ // Outer for loop
+ for (let i = 0; i < 2; i++) {
+ let innerSum = myp5.float(0.0);
- // Inner for loop
- for (let j = 0; j < 3; j++) {
- innerSum = innerSum + 0.1; // 3 * 0.1 = 0.3 per outer iteration
- }
+ // Inner for loop
+ for (let j = 0; j < 3; j++) {
+ innerSum = innerSum + 0.1; // 3 * 0.1 = 0.3 per outer iteration
+ }
- // State modification between inner and outer loop
- innerSum = innerSum * 0.5; // Multiply by 0.5: 0.3 * 0.5 = 0.15
- total = total + innerSum; // Add to total: 2 * 0.15 = 0.3
- }
+ // State modification between inner and outer loop
+ innerSum = innerSum * 0.5; // Multiply by 0.5: 0.3 * 0.5 = 0.15
+ total = total + innerSum; // Add to total: 2 * 0.15 = 0.3
+ }
- inputs.color = [total, total, total, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [total, total, total, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2157,18 +2366,21 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle for loop using loop variable in calculations', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let sum = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let sum = myp5.float(0.0);
- for (let i = 1; i <= 3; i++) {
- sum = sum + (i * 0.1); // 1*0.1 + 2*0.1 + 3*0.1 = 0.6
- }
+ for (let i = 1; i <= 3; i++) {
+ sum = sum + i * 0.1; // 1*0.1 + 2*0.1 + 3*0.1 = 0.6
+ }
- inputs.color = [sum, sum, sum, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [sum, sum, sum, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2185,27 +2397,30 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle direct StrandsFor API usage', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let accumulator = myp5.float(0.0);
-
- const loopResult = myp5.strandsFor(
- () => 0,
- (loopVar) => loopVar < 4,
- (loopVar) => loopVar + 1,
- (loopVar, vars) => {
- let newValue = vars.accumulator.copy();
- newValue = newValue + 0.125;
- return { accumulator: newValue };
- },
- { accumulator: accumulator.copy() },
- );
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let accumulator = myp5.float(0.0);
+
+ const loopResult = myp5.strandsFor(
+ () => 0,
+ loopVar => loopVar < 4,
+ loopVar => loopVar + 1,
+ (loopVar, vars) => {
+ let newValue = vars.accumulator.copy();
+ newValue = newValue + 0.125;
+ return { accumulator: newValue };
+ },
+ { accumulator: accumulator.copy() }
+ );
- accumulator = loopResult.accumulator;
- inputs.color = [accumulator, accumulator, accumulator, 1.0];
- return inputs;
- });
- }, { myp5 });
+ accumulator = loopResult.accumulator;
+ inputs.color = [accumulator, accumulator, accumulator, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2221,22 +2436,25 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle for loop with break statement', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let color = 0;
- let maxIterations = 5;
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let color = 0;
+ let maxIterations = 5;
- for (let i = 0; i < 100; i++) {
- if (i >= maxIterations) {
- break;
+ for (let i = 0; i < 100; i++) {
+ if (i >= maxIterations) {
+ break;
+ }
+ color = color + 0.1;
}
- color = color + 0.1;
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2250,25 +2468,28 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle statements after for loop before return', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let avg = myp5.vec3(0.0);
- let total = 0.0;
-
- // Simulate blur-like accumulation in for loop
- for (let i = 0; i < 3; i++) {
- const sample = myp5.vec3(0.2, 0.1, 0.3);
- const weight = 1.0;
- avg += weight * sample;
- total += weight;
- }
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let avg = myp5.vec3(0.0);
+ let total = 0.0;
+
+ // Simulate blur-like accumulation in for loop
+ for (let i = 0; i < 3; i++) {
+ const sample = myp5.vec3(0.2, 0.1, 0.3);
+ const weight = 1.0;
+ avg += weight * sample;
+ total += weight;
+ }
- const blended = avg / total;
+ const blended = avg / total;
- inputs.color = [blended.x, blended.y, blended.z, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [blended.x, blended.y, blended.z, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -2276,30 +2497,33 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
// Expected result: (3 * [0.2, 0.1, 0.3]) / 3 = [0.2, 0.1, 0.3]
const pixelColor = myp5.get(25, 25);
- assert.approximately(pixelColor[0], 51, 5); // 0.2 * 255 ≈ 51
- assert.approximately(pixelColor[1], 25, 5); // 0.1 * 255 ≈ 25
- assert.approximately(pixelColor[2], 77, 5); // 0.3 * 255 ≈ 77
+ assert.approximately(pixelColor[0], 51, 5); // 0.2 * 255 ≈ 51
+ assert.approximately(pixelColor[1], 25, 5); // 0.1 * 255 ≈ 25
+ assert.approximately(pixelColor[2], 77, 5); // 0.3 * 255 ≈ 77
});
test('handle nested loops with accumulator modified in inner loop', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- let aliveNeighbours = 0;
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ let aliveNeighbours = 0;
- for (let xOff = -1; xOff <= 1; xOff++) {
- for (let yOff = -1; yOff <= 1; yOff++) {
- if (xOff != 0 || yOff != 0) {
- aliveNeighbours += 0.1;
+ for (let xOff = -1; xOff <= 1; xOff++) {
+ for (let yOff = -1; yOff <= 1; yOff++) {
+ if (xOff != 0 || yOff != 0) {
+ aliveNeighbours += 0.1;
+ }
}
}
- }
- // 8 neighbors (all except center): 8 * 0.1 = 0.8
- return [aliveNeighbours, aliveNeighbours, aliveNeighbours, 1];
- });
- }, { myp5 });
+ // 8 neighbors (all except center): 8 * 0.1 = 0.8
+ return [aliveNeighbours, aliveNeighbours, aliveNeighbours, 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0); // Red background
myp5.filter(testShader);
@@ -2316,20 +2540,26 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('does not leak shared variable metadata into later shaders', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const firstShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.varyingVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor(() => [myp5.abs(worldPos / 25), 1]);
- }, { myp5 });
+ const firstShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(() => [myp5.abs(worldPos / 25), 1]);
+ },
+ { myp5 }
+ );
- const secondShader = myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- myp5.filterColor.set([1, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ const secondShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ myp5.filterColor.set([1, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
assert.deepEqual(firstShader.hooks.varyingVariables, ['vec3 worldPos']);
assert.deepEqual(secondShader.hooks.varyingVariables, []);
@@ -2341,16 +2571,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.varyingVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor((c) => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255); // Make the background blue to tell it apart
myp5.noStroke();
@@ -2374,16 +2607,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.varyingVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos.xyz = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor((c) => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos.xyz = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255); // Make the background blue to tell it apart
myp5.noStroke();
@@ -2407,17 +2643,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using implicit varying name with two underscores, which should be remapped
- let __worldPos = myp5.varyingVec3();
- myp5.getWorldInputs(inputs => {
- __worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(__worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using implicit varying name with two underscores, which should be remapped
+ let __worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ __worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(__worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255);
myp5.noStroke();
@@ -2439,17 +2678,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using explicit varying name with two underscores, which should be remapped
- let worldPos = myp5.varyingVec3('__worldPos');
- myp5.getWorldInputs(inputs => {
- worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using explicit varying name with two underscores, which should be remapped
+ let worldPos = myp5.varyingVec3('__worldPos');
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255);
myp5.noStroke();
@@ -2471,17 +2713,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.varyingVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos = inputs.position.xyz;
- inputs.position.xyz = worldPos + [25, 25, 0];
- return inputs;
- });
- myp5.getFinalColor((c) => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ inputs.position.xyz = worldPos + [25, 25, 0];
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255); // Make the background blue to tell it apart
myp5.noStroke();
@@ -2502,20 +2747,23 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('handle passing a value between fragment hooks only', () => {
- myp5.createCanvas(50, 50, myp5.WEBGL);
- myp5.pixelDensity(1);
-
- const testShader = myp5.baseMaterialShader().modify(() => {
- let processedNormal = myp5.sharedVec3();
- myp5.getPixelInputs((inputs) => {
- processedNormal = myp5.normalize(inputs.normal);
- return inputs;
- });
- myp5.getFinalColor((c) => {
- // Use the processed normal to create a color - should be [0, 0, 1] for plane facing camera
- return [myp5.abs(processedNormal), 1];
- });
- }, { myp5 });
+ myp5.createCanvas(50, 50, myp5.WEBGL);
+ myp5.pixelDensity(1);
+
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let processedNormal = myp5.sharedVec3();
+ myp5.getPixelInputs(inputs => {
+ processedNormal = myp5.normalize(inputs.normal);
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ // Use the processed normal to create a color - should be [0, 0, 1] for plane facing camera
+ return [myp5.abs(processedNormal), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0); // Red background to distinguish from result
myp5.noStroke();
@@ -2524,8 +2772,8 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
// Normal of plane facing camera should be [0, 0, 1], so color should be [0, 0, 255]
const centerColor = myp5.get(25, 25);
- assert.approximately(centerColor[0], 0, 5); // Red component
- assert.approximately(centerColor[1], 0, 5); // Green component
+ assert.approximately(centerColor[0], 0, 5); // Red component
+ assert.approximately(centerColor[1], 0, 5); // Green component
assert.approximately(centerColor[2], 255, 5); // Blue component
});
@@ -2533,21 +2781,24 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let processedNormal = myp5.sharedVec3();
- myp5.objectInputs.begin();
- myp5.objectInputs.position += [0, 0, 0];
- myp5.objectInputs.end();
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let processedNormal = myp5.sharedVec3();
+ myp5.objectInputs.begin();
+ myp5.objectInputs.position += [0, 0, 0];
+ myp5.objectInputs.end();
- myp5.pixelInputs.begin();
- processedNormal = myp5.normalize(myp5.pixelInputs.normal);
- myp5.pixelInputs.end();
+ myp5.pixelInputs.begin();
+ processedNormal = myp5.normalize(myp5.pixelInputs.normal);
+ myp5.pixelInputs.end();
- myp5.finalColor.begin();
- // Use the processed normal to create a color - should be [0, 0, 1] for plane facing camera
- myp5.finalColor.set([myp5.abs(processedNormal), 1]);
- myp5.finalColor.end();
- }, { myp5 });
+ myp5.finalColor.begin();
+ // Use the processed normal to create a color - should be [0, 0, 1] for plane facing camera
+ myp5.finalColor.set([myp5.abs(processedNormal), 1]);
+ myp5.finalColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0); // Red background to distinguish from result
myp5.noStroke();
@@ -2556,8 +2807,8 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
// Normal of plane facing camera should be [0, 0, 1], so color should be [0, 0, 255]
const centerColor = myp5.get(25, 25);
- assert.approximately(centerColor[0], 0, 5); // Red component
- assert.approximately(centerColor[1], 0, 5); // Green component
+ assert.approximately(centerColor[0], 0, 5); // Red component
+ assert.approximately(centerColor[1], 0, 5); // Green component
assert.approximately(centerColor[2], 255, 5); // Blue component
});
@@ -2565,17 +2816,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using implicit shared name with two underscores, which should be remapped
- let __processedNormal = myp5.sharedVec3();
- myp5.getPixelInputs(inputs => {
- __processedNormal = myp5.normalize(inputs.normal);
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(__processedNormal), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using implicit shared name with two underscores, which should be remapped
+ let __processedNormal = myp5.sharedVec3();
+ myp5.getPixelInputs(inputs => {
+ __processedNormal = myp5.normalize(inputs.normal);
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(__processedNormal), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0);
myp5.noStroke();
@@ -2592,17 +2846,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using explicit shared name with two underscores, which should be remapped
- let processedNormal = myp5.sharedVec3('__processedNormal');
- myp5.getPixelInputs(inputs => {
- processedNormal = myp5.normalize(inputs.normal);
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(processedNormal), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using explicit shared name with two underscores, which should be remapped
+ let processedNormal = myp5.sharedVec3('__processedNormal');
+ myp5.getPixelInputs(inputs => {
+ processedNormal = myp5.normalize(inputs.normal);
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(processedNormal), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0);
myp5.noStroke();
@@ -2619,16 +2876,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
myp5.createCanvas(50, 50, myp5.WEBGL);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.sharedVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor((c) => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255); // Make the background blue to tell it apart
myp5.noStroke();
@@ -2653,12 +2913,15 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle getColor hook with non-struct return type', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- // Simple test - just return a constant color
- return [1.0, 0.5, 0.0, 1.0]; // Orange color
- });
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ // Simple test - just return a constant color
+ return [1.0, 0.5, 0.0, 1.0]; // Orange color
+ });
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(0, 0, 255); // Blue background
@@ -2670,43 +2933,54 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
const pixelColor = myp5.get(25, 25);
assert.approximately(pixelColor[0], 255, 5); // Red channel should be 255
assert.approximately(pixelColor[1], 127, 5); // Green channel should be ~127
- assert.approximately(pixelColor[2], 0, 5); // Blue channel should be 0
+ assert.approximately(pixelColor[2], 0, 5); // Blue channel should be 0
});
test('simple vector multiplication in filter shader', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- // Test simple scalar * vector operation
- const scalar = 0.5;
- const vector = [1, 2];
- const result = scalar * vector;
- return [result.x, result.y, 0, 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ // Test simple scalar * vector operation
+ const scalar = 0.5;
+ const vector = [1, 2];
+ const result = scalar * vector;
+ return [result.x, result.y, 0, 1];
+ });
+ },
+ { myp5 }
+ );
});
test('handle complex filter shader with for loop and vector operations', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- const r = myp5.uniformFloat(() => 3); // Small value for testing
- myp5.getColor((inputs, canvasContent) => {
- let sum = [0, 0, 0, 0];
- let samples = 1;
-
- for (let i = 0; i < r; i++) {
- samples++;
- sum += myp5.texture(canvasContent, inputs.texCoord + (i / r) * [
- myp5.sin(4 * myp5.PI * i / r),
- myp5.cos(4 * myp5.PI * i / r)
- ]);
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ const r = myp5.uniformFloat(() => 3); // Small value for testing
+ myp5.getColor((inputs, canvasContent) => {
+ let sum = [0, 0, 0, 0];
+ let samples = 1;
+
+ for (let i = 0; i < r; i++) {
+ samples++;
+ sum += myp5.texture(
+ canvasContent,
+ inputs.texCoord +
+ (i / r) *
+ [
+ myp5.sin((4 * myp5.PI * i) / r),
+ myp5.cos((4 * myp5.PI * i) / r)
+ ]
+ );
+ }
- return sum / samples;
- });
- }, { myp5 });
+ return sum / samples;
+ });
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(255, 0, 0); // Red background
@@ -2727,26 +3001,36 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('noiseDetail state does not leak across modify calls', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const firstShader = myp5.baseFilterShader().modify(() => {
- // changes the default noiseDetail settings
- myp5.noiseDetail(7, 0.2);
- myp5.getColor(() => [myp5.noise(10), 0, 0, 1]);
- }, { myp5 });
+ const firstShader = myp5.baseFilterShader().modify(
+ () => {
+ // changes the default noiseDetail settings
+ myp5.noiseDetail(7, 0.2);
+ myp5.getColor(() => [myp5.noise(10), 0, 0, 1]);
+ },
+ { myp5 }
+ );
- const secondShader = myp5.baseFilterShader().modify(() => {
- // does not change noiseDetail settings, so should use defaults
- myp5.getColor(() => [myp5.noise(10), 0, 0, 1]);
- }, { myp5 });
+ const secondShader = myp5.baseFilterShader().modify(
+ () => {
+ // does not change noiseDetail settings, so should use defaults
+ myp5.getColor(() => [myp5.noise(10), 0, 0, 1]);
+ },
+ { myp5 }
+ );
- const explicitDefaultShader = myp5.baseFilterShader().modify(() => {
- // uses the default noiseDetail settings
- myp5.noiseDetail(4, 0.5);
- myp5.getColor(() => [myp5.noise(10), 0, 0, 1]);
- }, { myp5 });
+ const explicitDefaultShader = myp5.baseFilterShader().modify(
+ () => {
+ // uses the default noiseDetail settings
+ myp5.noiseDetail(4, 0.5);
+ myp5.getColor(() => [myp5.noise(10), 0, 0, 1]);
+ },
+ { myp5 }
+ );
const firstHook = firstShader.hooks.fragment['vec4 getColor'];
const secondHook = secondShader.hooks.fragment['vec4 getColor'];
- const defaultHook = explicitDefaultShader.hooks.fragment['vec4 getColor'];
+ const defaultHook =
+ explicitDefaultShader.hooks.fragment['vec4 getColor'];
assert.notStrictEqual(firstHook, secondHook);
assert.strictEqual(secondHook, defaultHook);
@@ -2757,11 +3041,14 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
expect(() => {
myp5.createCanvas(50, 50, myp5.WEBGL);
const input = new Array(i).fill(10);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(input), 0, 0, 1];
- });
- }, { myp5, input });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(input), 0, 0, 1];
+ });
+ },
+ { myp5, input }
+ );
myp5.shader(testShader);
myp5.plane(10, 10);
}).not.toThrowError();
@@ -2771,11 +3058,14 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
expect(() => {
myp5.createCanvas(50, 50, myp5.WEBGL);
const input = new Array(i).fill(10);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(...input), 0, 0, 1];
- });
- }, { myp5, input });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(...input), 0, 0, 1];
+ });
+ },
+ { myp5, input }
+ );
myp5.shader(testShader);
myp5.plane(10, 10);
}).not.toThrowError();
@@ -2787,11 +3077,14 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
expect(() => {
myp5.createCanvas(50, 50, myp5.WEBGL);
const input = new Array(i).fill(10);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(input), 0, 0, 1];
- });
- }, { myp5, input });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(input), 0, 0, 1];
+ });
+ },
+ { myp5, input }
+ );
myp5.shader(testShader);
myp5.plane(10, 10);
}).toThrowError();
@@ -2801,11 +3094,14 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
expect(() => {
myp5.createCanvas(50, 50, myp5.WEBGL);
const input = new Array(i).fill(10);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(...input), 0, 0, 1];
- });
- }, { myp5, input });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(...input), 0, 0, 1];
+ });
+ },
+ { myp5, input }
+ );
myp5.shader(testShader);
myp5.plane(10, 10);
}).toThrowError();
@@ -2816,32 +3112,50 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('randomSeed state does not leak across modify calls', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const firstShader = myp5.baseFilterShader().modify(() => {
- myp5.randomSeed(12);
- myp5.getColor(() => [myp5.random(), 0, 0, 1]);
- }, { myp5 });
+ const firstShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.randomSeed(12);
+ myp5.getColor(() => [myp5.random(), 0, 0, 1]);
+ },
+ { myp5 }
+ );
- const secondShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => [myp5.random(), 0, 0, 1]);
- }, { myp5 });
+ const secondShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => [myp5.random(), 0, 0, 1]);
+ },
+ { myp5 }
+ );
- assert.strictEqual(firstShader.hooks.uniforms['float _p5_randomSeed'](), 12);
- assert.notStrictEqual(secondShader.hooks.uniforms['float _p5_randomSeed'](), 12);
+ assert.strictEqual(
+ firstShader.hooks.uniforms['float _p5_randomSeed'](),
+ 12
+ );
+ assert.notStrictEqual(
+ secondShader.hooks.uniforms['float _p5_randomSeed'](),
+ 12
+ );
});
test('instanceID varying does not leak across modify calls', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const firstShader = myp5.baseMaterialShader().modify(() => {
- myp5.getFinalColor(() => {
- const id = myp5.instanceID();
- return [id / 10, 0, 0, 1];
- });
- }, { myp5 });
+ const firstShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getFinalColor(() => {
+ const id = myp5.instanceID();
+ return [id / 10, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
- const secondShader = myp5.baseMaterialShader().modify(() => {
- myp5.getFinalColor(() => [1, 0, 0, 1]);
- }, { myp5 });
+ const secondShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getFinalColor(() => [1, 0, 0, 1]);
+ },
+ { myp5 }
+ );
assert.isDefined(firstShader.hooks.instanceIDVarying);
assert.isNull(secondShader.hooks.instanceIDVarying);
@@ -2850,11 +3164,14 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('Can use begin/end API for hooks with result', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor.begin();
- myp5.getColor.set([1.0, 0.5, 0.0, 1.0]);
- myp5.getColor.end();
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor.begin();
+ myp5.getColor.set([1.0, 0.5, 0.0, 1.0]);
+ myp5.getColor.end();
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(0, 0, 255); // Blue background
@@ -2872,11 +3189,14 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('Can use begin/end API for hooks with hook alias', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- myp5.filterColor.set([1.0, 0.5, 0.0, 1.0]);
- myp5.filterColor.end();
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ myp5.filterColor.set([1.0, 0.5, 0.0, 1.0]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(0, 0, 255); // Blue background
@@ -2894,18 +3214,21 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('Can use begin/end API for hooks modifying inputs', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs.begin();
- myp5.getPixelInputs.color = [1.0, 0.5, 0.0, 1.0];
- myp5.getPixelInputs.end();
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs.begin();
+ myp5.getPixelInputs.color = [1.0, 0.5, 0.0, 1.0];
+ myp5.getPixelInputs.end();
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(0, 0, 255); // Blue background
// Draw a fullscreen rectangle
myp5.noStroke();
- myp5.fill('red')
+ myp5.fill('red');
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -2919,13 +3242,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('Can use begin/end API for hooks with struct access', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- let c = myp5.getTexture(myp5.filterColor.canvasContent, myp5.filterColor.texCoord);
- c.r = 1;
- myp5.filterColor.set(c);
- myp5.filterColor.end();
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ let c = myp5.getTexture(
+ myp5.filterColor.canvasContent,
+ myp5.filterColor.texCoord
+ );
+ c.r = 1;
+ myp5.filterColor.set(c);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(0, 0, 255); // Blue background
@@ -2943,18 +3272,21 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('Can use begin/end API for hooks with get* prefix removed', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.pixelInputs.begin();
- myp5.pixelInputs.color = [1.0, 0.5, 0.0, 1.0];
- myp5.pixelInputs.end();
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.pixelInputs.begin();
+ myp5.pixelInputs.color = [1.0, 0.5, 0.0, 1.0];
+ myp5.pixelInputs.end();
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(0, 0, 255); // Blue background
// Draw a fullscreen rectangle
myp5.noStroke();
- myp5.fill('red')
+ myp5.fill('red');
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -2968,16 +3300,19 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle .set() in if-else branches with flat API', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- let value = 1;
- if (value > 0.5) {
- myp5.filterColor.set([1, 0, 0, 1]);
- } else {
- myp5.filterColor.set([0, 1, 0, 1]);
- }
- myp5.filterColor.end();
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ let value = 1;
+ if (value > 0.5) {
+ myp5.filterColor.set([1, 0, 0, 1]);
+ } else {
+ myp5.filterColor.set([0, 1, 0, 1]);
+ }
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -2991,15 +3326,18 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle .set() in for loop with flat API', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- for (let i = 0; i < 3; i++) {
- if (i === 2) {
- myp5.filterColor.set([i/2, 0, 0, 1]);
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ for (let i = 0; i < 3; i++) {
+ if (i === 2) {
+ myp5.filterColor.set([i / 2, 0, 0, 1]);
+ }
}
- }
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -3013,19 +3351,24 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle uniformFloat with control flow in callback', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- // Uniform callback with an if-statement and multiple return paths
- const pastOneSecond = myp5.uniformFloat(() => {
- if (myp5.frameCount > 1000) {
- return 1;
- }
- return 0;
- });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ // Uniform callback with an if-statement and multiple return paths
+ const pastOneSecond = myp5.uniformFloat(() => {
+ if (myp5.frameCount > 1000) {
+ return 1;
+ }
+ return 0;
+ });
- myp5.filterColor.begin();
- myp5.filterColor.set(myp5.mix([1, 0, 0, 1], [0, 1, 0, 1], pastOneSecond));
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.filterColor.begin();
+ myp5.filterColor.set(
+ myp5.mix([1, 0, 0, 1], [0, 1, 0, 1], pastOneSecond)
+ );
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -3040,20 +3383,23 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle uniformFloat with for loop in callback', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- // Uniform callback with a for loop accumulating a value
- const brightness = myp5.uniformFloat(() => {
- let sum = 0;
- for (let i = 0; i < 3; i++) {
- sum += i;
- }
- return sum / 10; // 0+1+2=3, 3/10=0.3
- });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ // Uniform callback with a for loop accumulating a value
+ const brightness = myp5.uniformFloat(() => {
+ let sum = 0;
+ for (let i = 0; i < 3; i++) {
+ sum += i;
+ }
+ return sum / 10; // 0+1+2=3, 3/10=0.3
+ });
- myp5.filterColor.begin();
- myp5.filterColor.set([brightness, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.filterColor.begin();
+ myp5.filterColor.set([brightness, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -3068,17 +3414,20 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle uniformFloat with sub-function call in callback', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- // Uniform callback that calls a sub-function
- const brightness = myp5.uniformFloat(() => {
- const getValue = () => 0.6;
- return getValue();
- });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ // Uniform callback that calls a sub-function
+ const brightness = myp5.uniformFloat(() => {
+ const getValue = () => 0.6;
+ return getValue();
+ });
- myp5.filterColor.begin();
- myp5.filterColor.set([brightness, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.filterColor.begin();
+ myp5.filterColor.set([brightness, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -3093,19 +3442,24 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle uniformFloat with control flow in non-inline callback', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- function pastOneSecondValue() {
- if (myp5.frameCount > 1000) {
- return 1;
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ function pastOneSecondValue() {
+ if (myp5.frameCount > 1000) {
+ return 1;
+ }
+ return 0;
}
- return 0;
- }
- const pastOneSecond = myp5.uniformFloat(pastOneSecondValue);
+ const pastOneSecond = myp5.uniformFloat(pastOneSecondValue);
- myp5.filterColor.begin();
- myp5.filterColor.set(myp5.mix([1, 0, 0, 1], [0, 1, 0, 1], pastOneSecond));
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.filterColor.begin();
+ myp5.filterColor.set(
+ myp5.mix([1, 0, 0, 1], [0, 1, 0, 1], pastOneSecond)
+ );
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -3120,20 +3474,23 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle uniformFloat with for loop in non-inline callback', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- function brightnessValue() {
- let sum = 0;
- for (let i = 0; i < 3; i++) {
- sum += i;
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ function brightnessValue() {
+ let sum = 0;
+ for (let i = 0; i < 3; i++) {
+ sum += i;
+ }
+ return sum / 10; // 0+1+2=3, 3/10=0.3
}
- return sum / 10; // 0+1+2=3, 3/10=0.3
- }
- const brightness = myp5.uniformFloat(brightnessValue);
+ const brightness = myp5.uniformFloat(brightnessValue);
- myp5.filterColor.begin();
- myp5.filterColor.set([brightness, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.filterColor.begin();
+ myp5.filterColor.set([brightness, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -3148,19 +3505,22 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle uniformFloat with sub-function call in non-inline callback', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- function getValue() {
- return 0.6;
- }
- function brightnessValue() {
- return getValue();
- }
- const brightness = myp5.uniformFloat(brightnessValue);
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ function getValue() {
+ return 0.6;
+ }
+ function brightnessValue() {
+ return getValue();
+ }
+ const brightness = myp5.uniformFloat(brightnessValue);
- myp5.filterColor.begin();
- myp5.filterColor.set([brightness, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.filterColor.begin();
+ myp5.filterColor.set([brightness, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -3175,18 +3535,21 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
test('handle false .set() in if with content afterwards with flat API', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- let value = 1;
- if (value < 0.5) {
- myp5.filterColor.set([1, 0, 0, 1]);
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ let value = 1;
+ if (value < 0.5) {
+ myp5.filterColor.set([1, 0, 0, 1]);
+ }
- let otherValue = 0.2;
- otherValue *= 2;
- myp5.filterColor.set([otherValue, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ let otherValue = 0.2;
+ otherValue *= 2;
+ myp5.filterColor.set([otherValue, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -3198,60 +3561,72 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca
});
test('allows scalar broadcast when assigning a scalar to a sharedVec3 (bridge)', async () => {
- await myp5.createCanvas(5, 5, myp5.WEBGL);
+ await myp5.createCanvas(5, 5, myp5.WEBGL);
- expect(() => {
- myp5.baseMaterialShader().modify(() => {
- let worldPosX = myp5.sharedVec3();
- myp5.getWorldInputs(inputs => {
- worldPosX = inputs.position.x; // scalar → vec3, valid broadcast
- return inputs;
- });
- },{myp5});
- }).not.toThrow();
-});
+ expect(() => {
+ myp5.baseMaterialShader().modify(
+ () => {
+ let worldPosX = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPosX = inputs.position.x; // scalar → vec3, valid broadcast
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
+ }).not.toThrow();
+ });
-test('reports a friendly error when assigning a vec2 to a sharedVec3 (bridge)', async () => {
- await myp5.createCanvas(5, 5, myp5.WEBGL);
+ test('reports a friendly error when assigning a vec2 to a sharedVec3 (bridge)', async () => {
+ await myp5.createCanvas(5, 5, myp5.WEBGL);
- expect(() => {
- myp5.baseMaterialShader().modify(() => {
- let myVec = myp5.sharedVec3();
- myp5.getWorldInputs(inputs => {
- myVec = inputs.position.xy; // vec2 → vec3 mismatch
- return inputs;
- });
- },{myp5});
- }).toThrow(/dimension mismatch/);
-});
+ expect(() => {
+ myp5.baseMaterialShader().modify(
+ () => {
+ let myVec = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ myVec = inputs.position.xy; // vec2 → vec3 mismatch
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
+ }).toThrow(/dimension mismatch/);
+ });
-test('reports a friendly error on dimension mismatch via swizzle write (bridgeSwizzle)', async () => {
- await myp5.createCanvas(5, 5, myp5.WEBGL);
+ test('reports a friendly error on dimension mismatch via swizzle write (bridgeSwizzle)', async () => {
+ await myp5.createCanvas(5, 5, myp5.WEBGL);
- expect(() => {
- myp5.baseMaterialShader().modify(() => {
- let myVec = myp5.sharedVec3();
- myp5.getWorldInputs(inputs => {
- myVec.xy = inputs.position; // vec3 → 2-component swizzle mismatch
- return inputs;
- });
- },{myp5});
- }).toThrow(/dimension mismatch/);
-});
+ expect(() => {
+ myp5.baseMaterialShader().modify(
+ () => {
+ let myVec = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ myVec.xy = inputs.position; // vec3 → 2-component swizzle mismatch
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
+ }).toThrow(/dimension mismatch/);
+ });
-test('does not error when shared variable assignment dimensions match', async () => {
- await myp5.createCanvas(5, 5, myp5.WEBGL);
+ test('does not error when shared variable assignment dimensions match', async () => {
+ await myp5.createCanvas(5, 5, myp5.WEBGL);
- expect(() => {
- myp5.baseMaterialShader().modify(() => {
- let myVec = myp5.sharedVec3();
- myp5.getWorldInputs(inputs => {
- myVec = inputs.position; // vec3 → vec3, OK
- return inputs;
- });
- },{myp5});
- }).not.toThrow();
-});
+ expect(() => {
+ myp5.baseMaterialShader().modify(
+ () => {
+ let myVec = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ myVec = inputs.position; // vec3 → vec3, OK
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
+ }).not.toThrow();
+ });
});
suite('p5.strands error messages', () => {
@@ -3277,12 +3652,21 @@ test('does not error when shared variable assignment dimensions match', async ()
myp5.createCanvas(50, 50, myp5.WEBGL);
try {
- myp5.baseMaterialShader().modify(() => {
- myp5.getWorldInputs(() => [1, 2, 3, 4]); // vec4 instead of Vertex struct
- }, { myp5 });
- } catch (e) { /* expected */ }
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getWorldInputs(() => [1, 2, 3, 4]); // vec4 instead of Vertex struct
+ },
+ { myp5 }
+ );
+ } catch (e) {
+ /* expected */
+ }
- assert.isAbove(mockUserError.mock.calls.length, 0, 'FES.userError should have been called');
+ assert.isAbove(
+ mockUserError.mock.calls.length,
+ 0,
+ 'FES.userError should have been called'
+ );
const errMsg = mockUserError.mock.calls[0][1];
assert.notInclude(errMsg, 'a undefined'); //
assert.include(errMsg, 'float4');
@@ -3295,14 +3679,21 @@ test('does not error when shared variable assignment dimensions match', async ()
myp5.createCanvas(50, 50, myp5.WEBGL);
try {
- myp5.baseMaterialShader().modify(() => {
- myp5.getFinalColor((c) => [c.r, c.g, c.b]); // vec3 instead of vec4
- }, { myp5 });
- } catch (e) { /* expected */ }
-
-
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getFinalColor(c => [c.r, c.g, c.b]); // vec3 instead of vec4
+ },
+ { myp5 }
+ );
+ } catch (e) {
+ /* expected */
+ }
- assert.isAbove(mockUserError.mock.calls.length, 0, 'FES.userError should have been called, btw: '+globalThis.FESCalled);
+ assert.isAbove(
+ mockUserError.mock.calls.length,
+ 0,
+ 'FES.userError should have been called, btw: ' + globalThis.FESCalled
+ );
const errMsg = mockUserError.mock.calls[0][1];
assert.include(errMsg, 'float3');
assert.include(errMsg, 'float4');
@@ -3312,14 +3703,23 @@ test('does not error when shared variable assignment dimensions match', async ()
myp5.createCanvas(50, 50, myp5.WEBGL);
try {
- myp5.baseMaterialShader().modify(() => {
- myp5.getWorldInputs((inputs) => {
- return { position: inputs.position };
- });
- }, { myp5 });
- } catch (e) { /* expected */ }
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getWorldInputs(inputs => {
+ return { position: inputs.position };
+ });
+ },
+ { myp5 }
+ );
+ } catch (e) {
+ /* expected */
+ }
- assert.isAbove(mockUserError.mock.calls.length, 0, 'FES.userError should have been called');
+ assert.isAbove(
+ mockUserError.mock.calls.length,
+ 0,
+ 'FES.userError should have been called'
+ );
const errMsg = mockUserError.mock.calls[0][1];
assert.include(errMsg, 'Expected properties');
assert.include(errMsg, 'Received properties');
@@ -3329,17 +3729,27 @@ test('does not error when shared variable assignment dimensions match', async ()
myp5.createCanvas(50, 50, myp5.WEBGL);
try {
- myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- // float1 vs float4 - type mismatch
- const val = inputs.texCoord.x > 0.5 ? myp5.float(1.0) : [1, 0, 0, 1];
- inputs.color = [val, val, val, 1];
- return inputs;
- });
- }, { myp5 });
- } catch (e) { /* expected */ }
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ // float1 vs float4 - type mismatch
+ const val =
+ inputs.texCoord.x > 0.5 ? myp5.float(1.0) : [1, 0, 0, 1];
+ inputs.color = [val, val, val, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
+ } catch (e) {
+ /* expected */
+ }
- assert.isAbove(mockUserError.mock.calls.length, 0, 'FES.userError should have been called');
+ assert.isAbove(
+ mockUserError.mock.calls.length,
+ 0,
+ 'FES.userError should have been called'
+ );
const errMsg = mockUserError.mock.calls[0][1];
assert.include(errMsg, 'ternary');
assert.include(errMsg, 'float1');
@@ -3350,19 +3760,22 @@ test('does not error when shared variable assignment dimensions match', async ()
myp5.createCanvas(50, 50, myp5.WEBGL);
expectLoopProtectionError(() => {
- myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- {
- loopProtect.protect({ line: 11, reset: true });
- for (let i = 0; i < 10; i++) {
- if (loopProtect.protect({ line: 11 })) {
- break;
+ myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ {
+ loopProtect.protect({ line: 11, reset: true });
+ for (let i = 0; i < 10; i++) {
+ if (loopProtect.protect({ line: 11 })) {
+ break;
+ }
}
}
- }
- myp5.filterColor.set([1, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.filterColor.set([1, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
});
});
@@ -3370,14 +3783,17 @@ test('does not error when shared variable assignment dimensions match', async ()
myp5.createCanvas(50, 50, myp5.WEBGL);
expectLoopProtectionError(() => {
- myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- for (let i = 0; i < 10; i++) {
- window.$OP && $OP.loopProtect({ line: 11, ch: 2 });
- }
- myp5.filterColor.set([1, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
+ myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ for (let i = 0; i < 10; i++) {
+ window.$OP && $OP.loopProtect({ line: 11, ch: 2 });
+ }
+ myp5.filterColor.set([1, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
+ );
});
});
@@ -3385,15 +3801,26 @@ test('does not error when shared variable assignment dimensions match', async ()
myp5.createCanvas(50, 50, myp5.WEBGL);
try {
- myp5.baseMaterialShader().modify(() => {
- myp5.getWorldInputs.begin();
- myp5.getWorldInputs.end();
- const pos = myp5.getWorldInputs.position;
- }, { myp5 });
- } catch (e) { /* expected */ }
-
- assert.isAbove(mockUserError.mock.calls.length, 0, 'FES.userError should have been called');
- const scopeCall = mockUserError.mock.calls.find(call => call[0] === 'scope error');
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getWorldInputs.begin();
+ myp5.getWorldInputs.end();
+ const pos = myp5.getWorldInputs.position;
+ },
+ { myp5 }
+ );
+ } catch (e) {
+ /* expected */
+ }
+
+ assert.isAbove(
+ mockUserError.mock.calls.length,
+ 0,
+ 'FES.userError should have been called'
+ );
+ const scopeCall = mockUserError.mock.calls.find(
+ call => call[0] === 'scope error'
+ );
assert.isDefined(scopeCall, 'scope error should have been called');
const errMsg = scopeCall[1];
assert.include(errMsg, 'worldInputs');
diff --git a/test/unit/webgl/p5.Texture.js b/test/unit/webgl/p5.Texture.js
index ea566ddc82..7c3d79bc36 100644
--- a/test/unit/webgl/p5.Texture.js
+++ b/test/unit/webgl/p5.Texture.js
@@ -1,6 +1,6 @@
import p5 from '../../../src/app.js';
-suite('p5.Texture', function() {
+suite('p5.Texture', function () {
var myp5;
var texImg1;
var texImg2;
@@ -10,12 +10,12 @@ suite('p5.Texture', function() {
var canvas;
let prevPixelRatio;
- beforeEach(function() {
+ beforeEach(function () {
prevPixelRatio = window.devicePixelRatio;
window.devicePixelRatio = 1;
return new Promise(done => {
- myp5 = new p5(function(p) {
- p.setup = async function() {
+ myp5 = new p5(function (p) {
+ p.setup = async function () {
canvas = p.createCanvas(100, 100, p.WEBGL);
p.pixelDensity(1);
texImg1 = p.createGraphics(2, 2, p.WEBGL);
@@ -28,28 +28,38 @@ suite('p5.Texture', function() {
p.texture(imgElementPowerOfTwo);
resolve();
});
- }).then(() => new Promise(resolve => {
- p.createImg(texImg3.canvas.toDataURL(), '', 'anonymous', el => {
- el.size(50, 50);
- imgElementNotPowerOfTwo = el;
- p.texture(imgElementNotPowerOfTwo);
- resolve();
+ })
+ .then(
+ () =>
+ new Promise(resolve => {
+ p.createImg(
+ texImg3.canvas.toDataURL(),
+ '',
+ 'anonymous',
+ el => {
+ el.size(50, 50);
+ imgElementNotPowerOfTwo = el;
+ p.texture(imgElementNotPowerOfTwo);
+ resolve();
+ }
+ );
+ })
+ )
+ .then(() => {
+ p.texture(texImg1);
+ done();
});
- })).then(() => {
- p.texture(texImg1);
- done();
- });
};
});
});
});
- afterEach(function() {
+ afterEach(function () {
window.devicePixelRatio = prevPixelRatio;
myp5.remove();
});
- var testTextureSet = function(src) {
+ var testTextureSet = function (src) {
var lightShader = myp5._renderer._getLightShader();
var selectedShader = myp5._renderer._getFillShader();
console.log('first');
@@ -66,115 +76,221 @@ suite('p5.Texture', function() {
assert(tex.src === src, 'texture did not have expected image as source');
};
- suite('p5.Texture', function() {
+ suite('p5.Texture', function () {
let texParamSpy;
beforeEach(() => {
- texParamSpy = vi.spyOn(myp5._renderer.GL, 'texParameteri');
+ texParamSpy = vi.spyOn(myp5._renderer.GL, 'texParameteri');
});
afterEach(() => {
vi.restoreAllMocks();
});
- test('Create and cache a single texture with p5.Image', function() {
+ test('Create and cache a single texture with p5.Image', function () {
testTextureSet(texImg1);
});
- test('Create and cache multiple p5.Image textures', function() {
+ test('Create and cache multiple p5.Image textures', function () {
myp5.texture(texImg2);
testTextureSet(texImg2);
// other image was made into a texture in setup, should still be there!
//testTextureSet(texImg1);
});
- test('Set filter mode to linear', function() {
+ test('Set filter mode to linear', function () {
var tex = myp5._renderer.getTexture(texImg2);
tex.setInterpolation(myp5.LINEAR, myp5.LINEAR);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_MIN_FILTER, myp5._renderer.GL.LINEAR);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_MAG_FILTER, myp5._renderer.GL.LINEAR);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_MIN_FILTER,
+ myp5._renderer.GL.LINEAR
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_MAG_FILTER,
+ myp5._renderer.GL.LINEAR
+ );
});
- test('Set filter mode to nearest', function() {
+ test('Set filter mode to nearest', function () {
var tex = myp5._renderer.getTexture(texImg2);
tex.setInterpolation(myp5.NEAREST, myp5.NEAREST);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_MIN_FILTER, myp5._renderer.GL.NEAREST);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_MAG_FILTER, myp5._renderer.GL.NEAREST);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_MIN_FILTER,
+ myp5._renderer.GL.NEAREST
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_MAG_FILTER,
+ myp5._renderer.GL.NEAREST
+ );
});
- test('Set wrap mode to clamp', function() {
+ test('Set wrap mode to clamp', function () {
var tex = myp5._renderer.getTexture(texImg2);
tex.setWrapMode(myp5.CLAMP, myp5.CLAMP);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.CLAMP_TO_EDGE);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.CLAMP_TO_EDGE);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.CLAMP_TO_EDGE
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.CLAMP_TO_EDGE
+ );
});
- test('Set wrap mode to repeat', function() {
+ test('Set wrap mode to repeat', function () {
var tex = myp5._renderer.getTexture(texImg2);
tex.setWrapMode(myp5.REPEAT, myp5.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.REPEAT);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.REPEAT
+ );
});
- test('Set wrap mode to mirror', function() {
+ test('Set wrap mode to mirror', function () {
var tex = myp5._renderer.getTexture(texImg2);
tex.setWrapMode(myp5.MIRROR, myp5.MIRROR);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.MIRRORED_REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.MIRRORED_REPEAT);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.MIRRORED_REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.MIRRORED_REPEAT
+ );
});
- test('Set wrap mode REPEAT if src dimensions is powerOfTwo', function() {
+ test('Set wrap mode REPEAT if src dimensions is powerOfTwo', function () {
const tex = myp5._renderer.getTexture(imgElementPowerOfTwo);
tex.setWrapMode(myp5.REPEAT, myp5.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.REPEAT);
- });
- test(
- 'Set default wrap mode REPEAT if WEBGL2 and src dimensions != powerOfTwo',
- function() {
- const tex = myp5._renderer.getTexture(imgElementNotPowerOfTwo);
- tex.setWrapMode(myp5.REPEAT, myp5.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.REPEAT);
- }
- );
- test(
- 'Set default wrap mode CLAMP if WEBGL1 and src dimensions != powerOfTwo',
- function() {
- myp5.setAttributes({ version: 1 });
- texParamSpy = vi.spyOn(myp5._renderer.GL, 'texParameteri');
- const tex = myp5._renderer.getTexture(imgElementNotPowerOfTwo);
- tex.setWrapMode(myp5.REPEAT, myp5.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.CLAMP_TO_EDGE);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.CLAMP_TO_EDGE);
- }
- );
- test('Set textureMode to NORMAL', function() {
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.REPEAT
+ );
+ });
+ test('Set default wrap mode REPEAT if WEBGL2 and src dimensions != powerOfTwo', function () {
+ const tex = myp5._renderer.getTexture(imgElementNotPowerOfTwo);
+ tex.setWrapMode(myp5.REPEAT, myp5.REPEAT);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.REPEAT
+ );
+ });
+ test('Set default wrap mode CLAMP if WEBGL1 and src dimensions != powerOfTwo', function () {
+ myp5.setAttributes({ version: 1 });
+ texParamSpy = vi.spyOn(myp5._renderer.GL, 'texParameteri');
+ const tex = myp5._renderer.getTexture(imgElementNotPowerOfTwo);
+ tex.setWrapMode(myp5.REPEAT, myp5.REPEAT);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.CLAMP_TO_EDGE
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.CLAMP_TO_EDGE
+ );
+ });
+ test('Set textureMode to NORMAL', function () {
myp5.textureMode(myp5.NORMAL);
assert.deepEqual(myp5._renderer.states.textureMode, myp5.NORMAL);
});
- test('Set textureMode to IMAGE', function() {
+ test('Set textureMode to IMAGE', function () {
myp5.textureMode(myp5.IMAGE);
assert.deepEqual(myp5._renderer.states.textureMode, myp5.IMAGE);
});
- test('Set global wrap mode to clamp', function() {
+ test('Set global wrap mode to clamp', function () {
myp5.textureWrap(myp5.CLAMP);
var tex1 = myp5._renderer.getTexture(texImg1);
var tex2 = myp5._renderer.getTexture(texImg2);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.CLAMP_TO_EDGE);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.CLAMP_TO_EDGE);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.CLAMP_TO_EDGE);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.CLAMP_TO_EDGE);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.CLAMP_TO_EDGE
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.CLAMP_TO_EDGE
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.CLAMP_TO_EDGE
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.CLAMP_TO_EDGE
+ );
});
- test('Set global wrap mode to repeat', function() {
+ test('Set global wrap mode to repeat', function () {
myp5.textureWrap(myp5.REPEAT);
var tex1 = myp5._renderer.getTexture(texImg1);
var tex2 = myp5._renderer.getTexture(texImg2);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.REPEAT);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.REPEAT
+ );
});
- test('Set global wrap mode to mirror', function() {
+ test('Set global wrap mode to mirror', function () {
myp5.textureWrap(myp5.MIRROR);
var tex1 = myp5._renderer.getTexture(texImg1);
var tex2 = myp5._renderer.getTexture(texImg2);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.MIRRORED_REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.MIRRORED_REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, myp5._renderer.GL.MIRRORED_REPEAT);
- expect(texParamSpy).toHaveBeenCalledWith(myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_T, myp5._renderer.GL.MIRRORED_REPEAT);
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.MIRRORED_REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.MIRRORED_REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_S,
+ myp5._renderer.GL.MIRRORED_REPEAT
+ );
+ expect(texParamSpy).toHaveBeenCalledWith(
+ myp5._renderer.GL.TEXTURE_2D,
+ myp5._renderer.GL.TEXTURE_WRAP_T,
+ myp5._renderer.GL.MIRRORED_REPEAT
+ );
});
- test('Handles changes to p5.Image size', function() {
+ test('Handles changes to p5.Image size', function () {
const tex = myp5._renderer.getTexture(texImg2);
expect(tex.width).to.equal(texImg2.width);
expect(tex.height).to.equal(texImg2.height);
@@ -183,7 +299,7 @@ suite('p5.Texture', function() {
expect(tex.width).to.equal(texImg2.width);
expect(tex.height).to.equal(texImg2.height);
});
- test('Handles changes to p5.Graphics size', function() {
+ test('Handles changes to p5.Graphics size', function () {
const tex = myp5._renderer.getTexture(texImg1);
expect(tex.width).to.equal(texImg1.width);
expect(tex.height).to.equal(texImg1.height);
@@ -192,7 +308,7 @@ suite('p5.Texture', function() {
expect(tex.width).to.equal(texImg1.width);
expect(tex.height).to.equal(texImg1.height);
});
- test('Handles changes to p5.Renderer size', function() {
+ test('Handles changes to p5.Renderer size', function () {
const tex = texImg1._renderer.getTexture(canvas);
expect(tex.width).to.equal(canvas.width);
expect(tex.height).to.equal(canvas.height);
diff --git a/test/unit/webgpu/p5.Framebuffer.js b/test/unit/webgpu/p5.Framebuffer.js
index ccbadbc7a0..e395849051 100644
--- a/test/unit/webgpu/p5.Framebuffer.js
+++ b/test/unit/webgpu/p5.Framebuffer.js
@@ -1,31 +1,31 @@
import p5 from '../../../src/app.js';
-import rendererWebGPU from "../../../src/webgpu/p5.RendererWebGPU";
+import rendererWebGPU from '../../../src/webgpu/p5.RendererWebGPU';
p5.registerAddon(rendererWebGPU);
-suite('WebGPU p5.Framebuffer', function() {
+suite('WebGPU p5.Framebuffer', function () {
let myp5;
let prevPixelRatio;
- beforeAll(async function() {
+ beforeAll(async function () {
prevPixelRatio = window.devicePixelRatio;
window.devicePixelRatio = 1;
- myp5 = new p5(function(p) {
- p.setup = function() {};
+ myp5 = new p5(function (p) {
+ p.setup = function () {};
});
});
- beforeEach(async function() {
+ beforeEach(async function () {
await myp5.createCanvas(10, 10, 'webgpu');
- })
+ });
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
window.devicePixelRatio = prevPixelRatio;
});
- suite('Creation and basic properties', function() {
- test('framebuffers can be created with WebGPU renderer', async function() {
+ suite('Creation and basic properties', function () {
+ test('framebuffers can be created with WebGPU renderer', async function () {
const fbo = myp5.createFramebuffer();
expect(fbo).to.be.an('object');
@@ -34,7 +34,7 @@ suite('WebGPU p5.Framebuffer', function() {
expect(fbo.autoSized()).to.equal(true);
});
- test('framebuffers can be created with custom dimensions', async function() {
+ test('framebuffers can be created with custom dimensions', async function () {
const fbo = myp5.createFramebuffer({ width: 20, height: 30 });
expect(fbo.width).to.equal(20);
@@ -42,14 +42,14 @@ suite('WebGPU p5.Framebuffer', function() {
expect(fbo.autoSized()).to.equal(false);
});
- test('framebuffers have color texture', async function() {
+ test('framebuffers have color texture', async function () {
const fbo = myp5.createFramebuffer();
expect(fbo.color).to.be.an('object');
expect(fbo.color.rawTexture).to.be.a('function');
});
- test('framebuffers can specify different formats', async function() {
+ test('framebuffers can specify different formats', async function () {
const fbo = myp5.createFramebuffer({
format: 'float',
channels: 'rgb'
@@ -61,8 +61,8 @@ suite('WebGPU p5.Framebuffer', function() {
});
});
- suite('Auto-sizing behavior', function() {
- test('auto-sized framebuffers change size with canvas', async function() {
+ suite('Auto-sizing behavior', function () {
+ test('auto-sized framebuffers change size with canvas', async function () {
myp5.pixelDensity(1);
const fbo = myp5.createFramebuffer();
@@ -78,7 +78,7 @@ suite('WebGPU p5.Framebuffer', function() {
expect(fbo.density).to.equal(2);
});
- test('manually-sized framebuffers do not change size with canvas', async function() {
+ test('manually-sized framebuffers do not change size with canvas', async function () {
myp5.pixelDensity(3);
const fbo = myp5.createFramebuffer({ width: 25, height: 30, density: 1 });
@@ -94,7 +94,7 @@ suite('WebGPU p5.Framebuffer', function() {
expect(fbo.density).to.equal(1);
});
- test('manually-sized framebuffers can be made auto-sized', async function() {
+ test('manually-sized framebuffers can be made auto-sized', async function () {
myp5.pixelDensity(1);
const fbo = myp5.createFramebuffer({ width: 25, height: 30, density: 2 });
@@ -115,8 +115,8 @@ suite('WebGPU p5.Framebuffer', function() {
});
});
- suite('Manual resizing', function() {
- test('framebuffers can be manually resized', async function() {
+ suite('Manual resizing', function () {
+ test('framebuffers can be manually resized', async function () {
myp5.pixelDensity(1);
const fbo = myp5.createFramebuffer();
@@ -130,7 +130,7 @@ suite('WebGPU p5.Framebuffer', function() {
expect(fbo.autoSized()).to.equal(false);
});
- test('resizing affects pixel density', async function() {
+ test('resizing affects pixel density', async function () {
myp5.pixelDensity(1);
const fbo = myp5.createFramebuffer();
@@ -145,8 +145,8 @@ suite('WebGPU p5.Framebuffer', function() {
});
});
- suite('Drawing functionality', function() {
- test('can draw to framebuffer with draw() method', async function() {
+ suite('Drawing functionality', function () {
+ test('can draw to framebuffer with draw() method', async function () {
const fbo = myp5.createFramebuffer();
myp5.background(0, 255, 0);
@@ -171,7 +171,7 @@ suite('WebGPU p5.Framebuffer', function() {
expect([...myp5.pixels.slice(0, 3)]).toEqual([0, 0, 255]);
});
- test('can use framebuffer as texture', async function() {
+ test('can use framebuffer as texture', async function () {
const fbo = myp5.createFramebuffer();
fbo.draw(() => {
@@ -186,8 +186,8 @@ suite('WebGPU p5.Framebuffer', function() {
});
});
- suite('Pixel access', function() {
- test('loadPixels returns a promise in WebGPU', async function() {
+ suite('Pixel access', function () {
+ test('loadPixels returns a promise in WebGPU', async function () {
const fbo = myp5.createFramebuffer();
fbo.draw(() => {
@@ -203,7 +203,7 @@ suite('WebGPU p5.Framebuffer', function() {
expect([...pixels.slice(0, 4)]).toEqual([255, 0, 0, 255]);
});
- test('pixels property is set after loadPixels resolves', async function() {
+ test('pixels property is set after loadPixels resolves', async function () {
const fbo = myp5.createFramebuffer();
fbo.draw(() => {
@@ -215,7 +215,7 @@ suite('WebGPU p5.Framebuffer', function() {
expect(fbo.pixels.length).to.equal(10 * 10 * 4);
});
- test('get() returns a promise for single pixel in WebGPU', async function() {
+ test('get() returns a promise for single pixel in WebGPU', async function () {
const fbo = myp5.createFramebuffer();
fbo.draw(() => {
@@ -231,7 +231,7 @@ suite('WebGPU p5.Framebuffer', function() {
expect([...color]).toEqual([100, 150, 200, 255]);
});
- test('get() returns a promise for region in WebGPU', async function() {
+ test('get() returns a promise for region in WebGPU', async function () {
const fbo = myp5.createFramebuffer();
fbo.draw(() => {
diff --git a/test/unit/webgpu/p5.RendererWebGPU.js b/test/unit/webgpu/p5.RendererWebGPU.js
index 3a21f174f6..87b8ef6b5d 100644
--- a/test/unit/webgpu/p5.RendererWebGPU.js
+++ b/test/unit/webgpu/p5.RendererWebGPU.js
@@ -1,34 +1,34 @@
import p5 from '../../../src/app.js';
-import rendererWebGPU from "../../../src/webgpu/p5.RendererWebGPU";
+import rendererWebGPU from '../../../src/webgpu/p5.RendererWebGPU';
p5.registerAddon(rendererWebGPU);
-suite('WebGPU p5.RendererWebGPU', function() {
+suite('WebGPU p5.RendererWebGPU', function () {
let myp5;
let prevPixelRatio;
- beforeAll(async function() {
+ beforeAll(async function () {
prevPixelRatio = window.devicePixelRatio;
window.devicePixelRatio = 1;
- myp5 = new p5(function(p) {
- p.setup = function() {};
+ myp5 = new p5(function (p) {
+ p.setup = function () {};
});
});
- beforeEach(async function() {
+ beforeEach(async function () {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
});
- afterEach(function() {
+ afterEach(function () {
myp5.remove();
});
- afterAll(function() {
+ afterAll(function () {
window.devicePixelRatio = prevPixelRatio;
});
- suite('Buffer Pooling', function() {
- test('drawing geometry twice reuses vertex buffers', async function() {
+ suite('Buffer Pooling', function () {
+ test('drawing geometry twice reuses vertex buffers', async function () {
// Create a simple geometry
const geom = myp5.buildGeometry(() => {
myp5.triangle(0, 0, 10, 0, 5, 10);
@@ -42,7 +42,8 @@ suite('WebGPU p5.RendererWebGPU', function() {
const poolForVertexBuffer = geom._vertexBufferPools?.vertexBuffer;
expect(poolForVertexBuffer).to.exist;
const initialPoolSize = poolForVertexBuffer.length;
- const initialInUseSize = geom._vertexBuffersInUse?.vertexBuffer?.length || 0;
+ const initialInUseSize =
+ geom._vertexBuffersInUse?.vertexBuffer?.length || 0;
// Draw the geometry again
myp5.background(255);
@@ -50,14 +51,17 @@ suite('WebGPU p5.RendererWebGPU', function() {
// Verify the pool hasn't grown - buffers should be reused
const finalPoolSize = poolForVertexBuffer.length;
- const finalInUseSize = geom._vertexBuffersInUse?.vertexBuffer?.length || 0;
+ const finalInUseSize =
+ geom._vertexBuffersInUse?.vertexBuffer?.length || 0;
// Pool size should stay the same or be smaller (buffers moved from pool to in-use)
// The total number of buffers (pool + in-use) should remain constant
- expect(initialPoolSize + initialInUseSize).to.equal(finalPoolSize + finalInUseSize);
+ expect(initialPoolSize + initialInUseSize).to.equal(
+ finalPoolSize + finalInUseSize
+ );
});
- test('freeGeometry causes new buffer allocation on next draw', async function() {
+ test('freeGeometry causes new buffer allocation on next draw', async function () {
// Create a simple geometry
const geom = myp5.buildGeometry(() => {
myp5.triangle(0, 0, 10, 0, 5, 10);
@@ -70,7 +74,8 @@ suite('WebGPU p5.RendererWebGPU', function() {
// Get initial buffer count
const poolForVertexBuffer = geom._vertexBufferPools?.vertexBuffer;
expect(poolForVertexBuffer).to.exist;
- const initialTotalBuffers = poolForVertexBuffer.length +
+ const initialTotalBuffers =
+ poolForVertexBuffer.length +
(geom._vertexBuffersInUse?.vertexBuffer?.length || 0);
// Free the geometry
@@ -81,7 +86,8 @@ suite('WebGPU p5.RendererWebGPU', function() {
myp5.model(geom);
// After freeGeometry, new buffers should be allocated
- const finalTotalBuffers = poolForVertexBuffer.length +
+ const finalTotalBuffers =
+ poolForVertexBuffer.length +
(geom._vertexBuffersInUse?.vertexBuffer?.length || 0);
// We should have more buffers now since freeGeometry marks geometry as dirty
@@ -89,7 +95,7 @@ suite('WebGPU p5.RendererWebGPU', function() {
expect(finalTotalBuffers).to.be.greaterThan(initialTotalBuffers);
});
- test('immediate mode geometry reuses buffers across frames', async function() {
+ test('immediate mode geometry reuses buffers across frames', async function () {
// Function to draw the same shape using immediate mode
const drawSameShape = () => {
myp5.background(255);
@@ -106,10 +112,12 @@ suite('WebGPU p5.RendererWebGPU', function() {
// Get the immediate mode geometry (shapeBuilder geometry)
const immediateGeom = myp5._renderer.shapeBuilder.geometry;
- const poolForVertexBuffer = immediateGeom._vertexBufferPools?.vertexBuffer;
+ const poolForVertexBuffer =
+ immediateGeom._vertexBufferPools?.vertexBuffer;
expect(poolForVertexBuffer).to.exist;
- const initialTotalBuffers = poolForVertexBuffer.length +
+ const initialTotalBuffers =
+ poolForVertexBuffer.length +
(immediateGeom._vertexBuffersInUse?.vertexBuffer?.length || 0);
// Draw the same shape for several more frames
@@ -118,31 +126,34 @@ suite('WebGPU p5.RendererWebGPU', function() {
await myp5._renderer.finishDraw();
// Check that total buffer count hasn't increased
- const currentTotalBuffers = poolForVertexBuffer.length +
+ const currentTotalBuffers =
+ poolForVertexBuffer.length +
(immediateGeom._vertexBuffersInUse?.vertexBuffer?.length || 0);
- expect(currentTotalBuffers).to.equal(initialTotalBuffers,
- `Buffer count should stay constant across frames (frame ${frame})`);
+ expect(currentTotalBuffers).to.equal(
+ initialTotalBuffers,
+ `Buffer count should stay constant across frames (frame ${frame})`
+ );
}
});
});
- suite('noSmooth()', function() {
- test('disables antialiasing on the main canvas framebuffer', async function() {
+ suite('noSmooth()', function () {
+ test('disables antialiasing on the main canvas framebuffer', async function () {
await myp5.noSmooth();
expect(myp5._renderer.mainFramebuffer.antialias).to.equal(false);
});
});
- suite('Stability', function() {
- test('pixelDensity() after setAttributes() should not crash', async function() {
+ suite('Stability', function () {
+ test('pixelDensity() after setAttributes() should not crash', async function () {
// This test simulates the issue where a synchronous call (pixelDensity)
// happens before an asynchronous initialization (setAttributes -> _resetContext)
// is complete.
await new Promise((resolve, reject) => {
try {
myp5 = new p5(p => {
- p.setup = async function() {
+ p.setup = async function () {
try {
await p.createCanvas(100, 100, p.WEBGPU);
@@ -168,8 +179,8 @@ suite('WebGPU p5.RendererWebGPU', function() {
});
});
- suite('StorageBuffer.read()', function() {
- test('reads back float array data', async function() {
+ suite('StorageBuffer.read()', function () {
+ test('reads back float array data', async function () {
const input = new Float32Array([1, 2, 3, 4]);
const buf = myp5.createStorage(input);
@@ -182,10 +193,10 @@ suite('WebGPU p5.RendererWebGPU', function() {
}
});
- test('reads back struct array data', async function() {
+ test('reads back struct array data', async function () {
const input = [
{ x: 1.0, y: 2.0 },
- { x: 3.0, y: 4.0 },
+ { x: 3.0, y: 4.0 }
];
const buf = myp5.createStorage(input);
@@ -199,7 +210,7 @@ suite('WebGPU p5.RendererWebGPU', function() {
}
});
- test('read after update returns new data', async function() {
+ test('read after update returns new data', async function () {
const buf = myp5.createStorage(new Float32Array([10, 20, 30]));
const updated = new Float32Array([100, 200, 300]);
buf.update(updated);
@@ -211,10 +222,10 @@ suite('WebGPU p5.RendererWebGPU', function() {
}
});
- test('reads back struct with vector fields as p5.Vector', async function() {
+ test('reads back struct with vector fields as p5.Vector', async function () {
const input = [
{ position: myp5.createVector(1, 2), speed: 5.0 },
- { position: myp5.createVector(3, 4), speed: 10.0 },
+ { position: myp5.createVector(3, 4), speed: 10.0 }
];
const buf = myp5.createStorage(input);
@@ -233,15 +244,18 @@ suite('WebGPU p5.RendererWebGPU', function() {
expect(result[1].speed).to.be.closeTo(10.0, 0.001);
});
- test('reads back data modified by a compute shader', async function() {
+ test('reads back data modified by a compute shader', async function () {
const input = new Float32Array([1, 2, 3, 4]);
const buf = myp5.createStorage(input);
- const computeShader = myp5.buildComputeShader(() => {
- const d = myp5.uniformStorage();
- const idx = myp5.index.x;
- d[idx] = d[idx] * 2;
- }, { myp5 });
+ const computeShader = myp5.buildComputeShader(
+ () => {
+ const d = myp5.uniformStorage();
+ const idx = myp5.index.x;
+ d[idx] = d[idx] * 2;
+ },
+ { myp5 }
+ );
computeShader.setUniform('d', buf);
myp5.compute(computeShader, 4);
@@ -255,8 +269,8 @@ suite('WebGPU p5.RendererWebGPU', function() {
});
});
- suite('StorageBuffer.set()', function() {
- test('updates a single float value at the given index', async function() {
+ suite('StorageBuffer.set()', function () {
+ test('updates a single float value at the given index', async function () {
const buf = myp5.createStorage(new Float32Array([1, 2, 3, 4]));
buf.set(2, 9.5);
@@ -268,11 +282,11 @@ suite('WebGPU p5.RendererWebGPU', function() {
expect(result[3]).to.be.closeTo(4, 0.001);
});
- test('updates a single struct element without touching neighbours', async function() {
+ test('updates a single struct element without touching neighbours', async function () {
const input = [
{ x: 1.0, y: 2.0 },
{ x: 3.0, y: 4.0 },
- { x: 5.0, y: 6.0 },
+ { x: 5.0, y: 6.0 }
];
const buf = myp5.createStorage(input);
@@ -293,7 +307,7 @@ suite('WebGPU p5.RendererWebGPU', function() {
expect(result[2].y).to.be.closeTo(6.0, 0.001);
});
- test('set() then read() reflects the new value immediately', async function() {
+ test('set() then read() reflects the new value immediately', async function () {
const buf = myp5.createStorage(new Float32Array([0, 0, 0]));
buf.set(0, 42);
@@ -301,39 +315,42 @@ suite('WebGPU p5.RendererWebGPU', function() {
expect(result[0]).to.be.closeTo(42, 0.001);
});
- test('throws on out-of-bounds index for float buffer', function() {
+ test('throws on out-of-bounds index for float buffer', function () {
const buf = myp5.createStorage(new Float32Array([1, 2, 3]));
expect(() => buf.set(10, 5.0)).to.throw();
});
- test('throws on out-of-bounds index for struct buffer', function() {
+ test('throws on out-of-bounds index for struct buffer', function () {
const buf = myp5.createStorage([{ x: 1.0 }, { x: 2.0 }]);
expect(() => buf.set(99, { x: 3.0 })).to.throw();
});
- test('throws when passing a non-number to a float buffer', function() {
+ test('throws when passing a non-number to a float buffer', function () {
const buf = myp5.createStorage(new Float32Array([1, 2, 3]));
expect(() => buf.set(0, { x: 1 })).to.throw();
});
- test('throws when passing a non-object to a struct buffer', function() {
+ test('throws when passing a non-object to a struct buffer', function () {
const buf = myp5.createStorage([{ x: 1.0 }, { x: 2.0 }]);
expect(() => buf.set(0, 42)).to.throw();
});
});
- suite('p5.strands', function() {
- test('a uniform whose name matches a hook parameter name does not break', async function() {
+ suite('p5.strands', function () {
+ test('a uniform whose name matches a hook parameter name does not break', async function () {
myp5.pixelDensity(1);
// 'color' is the WGSL parameter name of the getFinalColor hook's first argument.
// Creating a uniform with the same name used to cause a WGSL name clash.
- const myShader = myp5.baseColorShader().modify(() => {
- const color = myp5.uniformFloat('color', 0.5);
- myp5.finalColor.begin();
- myp5.finalColor.set([color, color, color, 1]);
- myp5.finalColor.end();
- }, { myp5 });
+ const myShader = myp5.baseColorShader().modify(
+ () => {
+ const color = myp5.uniformFloat('color', 0.5);
+ myp5.finalColor.begin();
+ myp5.finalColor.set([color, color, color, 1]);
+ myp5.finalColor.end();
+ },
+ { myp5 }
+ );
myp5.background(0);
myp5.noStroke();
diff --git a/test/unit/webgpu/p5.Shader.js b/test/unit/webgpu/p5.Shader.js
index 6fcadac59b..5c20a6b199 100644
--- a/test/unit/webgpu/p5.Shader.js
+++ b/test/unit/webgpu/p5.Shader.js
@@ -1,15 +1,15 @@
import p5 from '../../../src/app.js';
-import rendererWebGPU from "../../../src/webgpu/p5.RendererWebGPU";
+import rendererWebGPU from '../../../src/webgpu/p5.RendererWebGPU';
p5.registerAddon(rendererWebGPU);
-suite('WebGPU p5.Shader', function() {
+suite('WebGPU p5.Shader', function () {
var myp5;
- beforeAll(function() {
+ beforeAll(function () {
window.IS_MINIFIED = true;
- myp5 = new p5(function(p) {
- p.setup = function() {
+ myp5 = new p5(function (p) {
+ p.setup = function () {
p.createCanvas(100, 100, 'webgpu');
p.pointLight(250, 250, 250, 100, 100, 0);
p.ambientMaterial(250);
@@ -17,7 +17,7 @@ suite('WebGPU p5.Shader', function() {
});
});
- afterAll(function() {
+ afterAll(function () {
myp5.remove();
});
@@ -25,32 +25,41 @@ suite('WebGPU p5.Shader', function() {
test('builtin global accessors remain usable across modify calls and after failures', async () => {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
- myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- inputs.color = [myp5.mouseX / myp5.width, 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [myp5.mouseX / myp5.width, 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- throw new Error('intentional failure');
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ throw new Error('intentional failure');
+ },
+ { myp5 }
+ );
}).toThrowError('intentional failure');
- myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- // global accessors are still usable
- // meaning _builtinGlobalsAccessorsInstalled is not accidentally reset
- const mxInHook = myp5.mouseX;
- const wInHook = myp5.width;
- // inside modify hooks, global accessors return strands nodes
- assert.isTrue(mxInHook.isStrandsNode);
- assert.isTrue(wInHook.isStrandsNode);
- inputs.color = [1, 0, 0, 1];
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ // global accessors are still usable
+ // meaning _builtinGlobalsAccessorsInstalled is not accidentally reset
+ const mxInHook = myp5.mouseX;
+ const wInHook = myp5.width;
+ // inside modify hooks, global accessors return strands nodes
+ assert.isTrue(mxInHook.isStrandsNode);
+ assert.isTrue(wInHook.isStrandsNode);
+ inputs.color = [1, 0, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
// after modify finishes, global accessors go back to ordinary p5 values
assert.isNumber(myp5.mouseX);
@@ -59,17 +68,16 @@ suite('WebGPU p5.Shader', function() {
test('does not break when arrays are in uniform callbacks', async () => {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
- const myShader = myp5.baseMaterialShader().modify(() => {
- const size = myp5.uniformVector2(() => [myp5.width, myp5.height]);
- myp5.getPixelInputs(inputs => {
- inputs.color = [
- size / 1000,
- 0,
- 1
- ];
- return inputs;
- });
- }, { myp5 });
+ const myShader = myp5.baseMaterialShader().modify(
+ () => {
+ const size = myp5.uniformVector2(() => [myp5.width, myp5.height]);
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [size / 1000, 0, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
expect(() => {
myp5.shader(myShader);
myp5.plane(myp5.width, myp5.height);
@@ -79,23 +87,28 @@ suite('WebGPU p5.Shader', function() {
test('does not leak shared variable metadata into later shaders', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const firstShader = myp5.baseMaterialShader().modify(() => {
- let __worldPos = myp5.varyingVec3();
- myp5.getWorldInputs(inputs => {
- __worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor(() => [myp5.abs(__worldPos / 25), 1]);
- }, { myp5 });
-
- const secondShader = myp5.baseMaterialShader().modify(() => {
- myp5.getFinalColor(() => [1, 0, 0, 1]);
- }, { myp5 });
+ const firstShader = myp5.baseMaterialShader().modify(
+ () => {
+ let __worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ __worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(() => [myp5.abs(__worldPos / 25), 1]);
+ },
+ { myp5 }
+ );
- assert.deepEqual(
- firstShader.hooks.varyingVariables,
- [`${firstShader.hooks.shaderNameMap.externalToInternal.__worldPos}: vec3`]
+ const secondShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getFinalColor(() => [1, 0, 0, 1]);
+ },
+ { myp5 }
);
+
+ assert.deepEqual(firstShader.hooks.varyingVariables, [
+ `${firstShader.hooks.shaderNameMap.externalToInternal.__worldPos}: vec3`
+ ]);
assert.deepEqual(secondShader.hooks.varyingVariables, []);
assert.notInclude(secondShader.vertSrc(), '__worldPos');
assert.notInclude(secondShader.fragSrc(), '__worldPos');
@@ -103,19 +116,22 @@ suite('WebGPU p5.Shader', function() {
test('renames only problematic inferred uniform names', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // Names containing two underscores are treated as problematic because of GLSL
- // naming constraints, so p5.strands remaps them through a shared naming layer.
- // WGSL itself would allow this name, but we still test the behavior in WebGPU
- // because the remapping logic is shared across transpiler, strands API, codegen,
- // and runtime shader handling.
- const __val = myp5.uniformFloat(() => 0.8);
- const brightness = myp5.uniformFloat(() => 0.2);
- myp5.getPixelInputs(inputs => {
- inputs.color = [__val, brightness, brightness, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // Names containing two underscores are treated as problematic because of GLSL
+ // naming constraints, so p5.strands remaps them through a shared naming layer.
+ // WGSL itself would allow this name, but we still test the behavior in WebGPU
+ // because the remapping logic is shared across transpiler, strands API, codegen,
+ // and runtime shader handling.
+ const __val = myp5.uniformFloat(() => 0.8);
+ const brightness = myp5.uniformFloat(() => 0.2);
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [__val, brightness, brightness, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
assert.strictEqual(
testShader.hooks.shaderNameMap.externalToInternal.__val,
@@ -143,14 +159,17 @@ suite('WebGPU p5.Shader', function() {
test('handles implicit uniform names containing two underscores', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using implicit uniform name with two underscores, which should be remapped
- const __val = myp5.uniformFloat();
- myp5.getPixelInputs(inputs => {
- inputs.color = [__val, __val, __val, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using implicit uniform name with two underscores, which should be remapped
+ const __val = myp5.uniformFloat();
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [__val, __val, __val, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
// setting the uniform using the original name
testShader.setUniform('__val', 0.6);
@@ -168,14 +187,17 @@ suite('WebGPU p5.Shader', function() {
test('handles explicit uniform names containing two underscores', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using explicit uniform name with two underscores, which should be remapped
- const val = myp5.uniformFloat('__val');
- myp5.getPixelInputs(inputs => {
- inputs.color = [val, val, val, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using explicit uniform name with two underscores, which should be remapped
+ const val = myp5.uniformFloat('__val');
+ myp5.getPixelInputs(inputs => {
+ inputs.color = [val, val, val, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
// setting the uniform using the original name
testShader.setUniform('__val', 0.6);
@@ -192,21 +214,27 @@ suite('WebGPU p5.Shader', function() {
test('continues inferred uniform suffixes across chained modify calls', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const shader1 = myp5.baseMaterialShader().modify(() => {
- const __first = myp5.uniformFloat(() => 0.2);
- myp5.getPixelInputs(inputs => {
- inputs.color.r = __first;
- return inputs;
- });
- }, { myp5 });
+ const shader1 = myp5.baseMaterialShader().modify(
+ () => {
+ const __first = myp5.uniformFloat(() => 0.2);
+ myp5.getPixelInputs(inputs => {
+ inputs.color.r = __first;
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
- const shader2 = shader1.modify(() => {
- const __second = myp5.uniformFloat(() => 0.8);
- myp5.getWorldInputs(inputs => {
- inputs.position.y += __second * 0.0;
- return inputs;
- });
- }, { myp5 });
+ const shader2 = shader1.modify(
+ () => {
+ const __second = myp5.uniformFloat(() => 0.8);
+ myp5.getWorldInputs(inputs => {
+ inputs.position.y += __second * 0.0;
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
assert.strictEqual(
shader2.hooks.shaderNameMap.externalToInternal.__first,
@@ -216,57 +244,59 @@ suite('WebGPU p5.Shader', function() {
shader2.hooks.shaderNameMap.externalToInternal.__second,
'_p5_strands_1'
);
- assert.strictEqual(
- shader2.hooks.shaderNameState.nextSuffix,
- 2
- );
+ assert.strictEqual(shader2.hooks.shaderNameState.nextSuffix, 2);
});
test('does not leak shared/varying state into later modify calls', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const firstShader = myp5.baseMaterialShader().modify(() => {
- let __worldPos = myp5.varyingVec3();
- myp5.getWorldInputs(inputs => {
- __worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(__worldPos / 25), 1];
- });
- }, { myp5 });
-
- assert.deepEqual(
- firstShader.hooks.varyingVariables,
- [`${firstShader.hooks.shaderNameMap.externalToInternal.__worldPos}: vec3`]
+ const firstShader = myp5.baseMaterialShader().modify(
+ () => {
+ let __worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ __worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(__worldPos / 25), 1];
+ });
+ },
+ { myp5 }
);
- const secondShader = myp5.baseFilterShader().modify(() => {
- myp5.filterColor.begin();
- myp5.filterColor.set([1, 0, 0, 1]);
- myp5.filterColor.end();
- }, { myp5 });
-
- assert.deepEqual(
- secondShader.hooks.varyingVariables,
- []
+ assert.deepEqual(firstShader.hooks.varyingVariables, [
+ `${firstShader.hooks.shaderNameMap.externalToInternal.__worldPos}: vec3`
+ ]);
+
+ const secondShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.filterColor.begin();
+ myp5.filterColor.set([1, 0, 0, 1]);
+ myp5.filterColor.end();
+ },
+ { myp5 }
);
+
+ assert.deepEqual(secondShader.hooks.varyingVariables, []);
});
suite('if statement conditionals', () => {
test('handle simple if statement with true condition', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- if (condition > 0.5) {
- color = myp5.float(1.0); // set to white in if branch
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ if (condition > 0.5) {
+ color = myp5.float(1.0); // set to white in if branch
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -279,17 +309,20 @@ suite('WebGPU p5.Shader', function() {
test('handle simple if statement with simpler assignment', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = 1; // initial gray
- if (condition > 0.5) {
- color = 1; // set to white in if branch
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = 1; // initial gray
+ if (condition > 0.5) {
+ color = 1; // set to white in if branch
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -302,17 +335,20 @@ suite('WebGPU p5.Shader', function() {
test('handle simple if statement with false condition', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 0.0); // false condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- if (condition > 0.5) {
- color = myp5.float(1.0); // set to white in if branch
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 0.0); // false condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ if (condition > 0.5) {
+ color = myp5.float(1.0); // set to white in if branch
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -325,19 +361,22 @@ suite('WebGPU p5.Shader', function() {
test('handle if-else statement', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 0.0); // false condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- if (condition > 0.5) {
- color = myp5.float(1.0); // white for true
- } else {
- color = myp5.float(0.0); // black for false
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 0.0); // false condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ if (condition > 0.5) {
+ color = myp5.float(1.0); // white for true
+ } else {
+ color = myp5.float(0.0); // black for false
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -350,21 +389,24 @@ suite('WebGPU p5.Shader', function() {
test('handle multiple variable assignments in if statement', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let red = myp5.float(0.0);
- let green = myp5.float(0.0);
- let blue = myp5.float(0.0);
- if (condition > 0.5) {
- red = myp5.float(1.0);
- green = myp5.float(0.5);
- blue = myp5.float(0.0);
- }
- inputs.color = [red, green, blue, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let red = myp5.float(0.0);
+ let green = myp5.float(0.0);
+ let blue = myp5.float(0.0);
+ if (condition > 0.5) {
+ red = myp5.float(1.0);
+ green = myp5.float(0.5);
+ blue = myp5.float(0.0);
+ }
+ inputs.color = [red, green, blue, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -372,26 +414,29 @@ suite('WebGPU p5.Shader', function() {
const pixelColor = await myp5.get(25, 25);
assert.approximately(pixelColor[0], 255, 5); // Red channel should be 255
assert.approximately(pixelColor[1], 127, 5); // Green channel should be ~127
- assert.approximately(pixelColor[2], 0, 5); // Blue channel should be ~0
+ assert.approximately(pixelColor[2], 0, 5); // Blue channel should be ~0
});
test('handle modifications after if statement', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const condition = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0); // start with black
- if (condition > 0.5) {
- color = myp5.float(1.0); // set to white in if branch
- } else {
- color = myp5.float(0.5); // set to gray in else branch
- }
- // Modify the color after the if statement
- color = color * 0.5; // Should result in 0.5 * 1.0 = 0.5 (gray)
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const condition = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0); // start with black
+ if (condition > 0.5) {
+ color = myp5.float(1.0); // set to white in if branch
+ } else {
+ color = myp5.float(0.5); // set to gray in else branch
+ }
+ // Modify the color after the if statement
+ color = color * 0.5; // Should result in 0.5 * 1.0 = 0.5 (gray)
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -404,30 +449,33 @@ suite('WebGPU p5.Shader', function() {
test('handle modifications after if statement in both branches', async () => {
await myp5.createCanvas(100, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const uv = inputs.texCoord;
- const condition = uv.x > 0.5; // left half false, right half true
- let color = myp5.float(0.0);
- if (condition) {
- color = myp5.float(1.0); // white on right side
- } else {
- color = myp5.float(0.8); // light gray on left side
- }
- // Multiply by 0.5 after the if statement
- color = color * 0.5;
- // Right side: 1.0 * 0.5 = 0.5 (medium gray)
- // Left side: 0.8 * 0.5 = 0.4 (darker gray)
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const uv = inputs.texCoord;
+ const condition = uv.x > 0.5; // left half false, right half true
+ let color = myp5.float(0.0);
+ if (condition) {
+ color = myp5.float(1.0); // white on right side
+ } else {
+ color = myp5.float(0.8); // light gray on left side
+ }
+ // Multiply by 0.5 after the if statement
+ color = color * 0.5;
+ // Right side: 1.0 * 0.5 = 0.5 (medium gray)
+ // Left side: 0.8 * 0.5 = 0.4 (darker gray)
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
// Check left side (false condition)
- const leftPixel = await myp5.get(25, 25)
- const rightPixel = await myp5.get(75, 25)
+ const leftPixel = await myp5.get(25, 25);
+ const rightPixel = await myp5.get(75, 25);
assert.approximately(leftPixel[0], 102, 5); // 0.4 * 255 ≈ 102
// Check right side (true condition)
assert.approximately(rightPixel[0], 127, 5); // 0.5 * 255 ≈ 127
@@ -435,21 +483,24 @@ suite('WebGPU p5.Shader', function() {
test('handle if-else-if chains', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const value = myp5.uniformFloat(() => 0.5); // middle value
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
- if (value > 0.8) {
- color = myp5.float(1.0); // white for high values
- } else if (value > 0.3) {
- color = myp5.float(0.5); // gray for medium values
- } else {
- color = myp5.float(0.0); // black for low values
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const value = myp5.uniformFloat(() => 0.5); // middle value
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
+ if (value > 0.8) {
+ color = myp5.float(1.0); // white for high values
+ } else if (value > 0.3) {
+ color = myp5.float(0.5); // gray for medium values
+ } else {
+ color = myp5.float(0.0); // black for low values
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -462,21 +513,24 @@ suite('WebGPU p5.Shader', function() {
test('handle if-else-if chains in the else branch', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const value = myp5.uniformFloat(() => 0.2); // middle value
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
- if (value > 0.8) {
- color = myp5.float(1.0); // white for high values
- } else if (value > 0.3) {
- color = myp5.float(0.5); // gray for medium values
- } else {
- color = myp5.float(0.0); // black for low values
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const value = myp5.uniformFloat(() => 0.2); // middle value
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
+ if (value > 0.8) {
+ color = myp5.float(1.0); // white for high values
+ } else if (value > 0.3) {
+ color = myp5.float(0.5); // gray for medium values
+ } else {
+ color = myp5.float(0.0); // black for low values
+ }
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -489,29 +543,32 @@ suite('WebGPU p5.Shader', function() {
test('handle conditional assignment in if-else-if chains', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const val = myp5.uniformFloat(() => Math.PI * 8);
- myp5.getPixelInputs(inputs => {
- let shininess = 0
- let color = 0
- if (val > 5) {
- const elevation = myp5.sin(val)
- if (elevation > 0.4) {
- shininess = 0;
- } else if (elevation > 0.25) {
- shininess = 30;
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const val = myp5.uniformFloat(() => Math.PI * 8);
+ myp5.getPixelInputs(inputs => {
+ let shininess = 0;
+ let color = 0;
+ if (val > 5) {
+ const elevation = myp5.sin(val);
+ if (elevation > 0.4) {
+ shininess = 0;
+ } else if (elevation > 0.25) {
+ shininess = 30;
+ } else {
+ color = 1;
+ shininess = 100;
+ }
} else {
- color = 1;
- shininess = 100;
+ shininess += 25;
}
- } else {
- shininess += 25;
- }
- inputs.shininess = shininess;
- inputs.color = [color, color, color, 1];
- return inputs;
- });
- }, { myp5 });
+ inputs.shininess = shininess;
+ inputs.color = [color, color, color, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -524,24 +581,27 @@ suite('WebGPU p5.Shader', function() {
test('handle nested if statements', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const outerCondition = myp5.uniformFloat(() => 1.0); // true
- const innerCondition = myp5.uniformFloat(() => 1.0); // true
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
- if (outerCondition > 0.5) {
- if (innerCondition > 0.5) {
- color = myp5.float(1.0); // white for both conditions true
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const outerCondition = myp5.uniformFloat(() => 1.0); // true
+ const innerCondition = myp5.uniformFloat(() => 1.0); // true
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
+ if (outerCondition > 0.5) {
+ if (innerCondition > 0.5) {
+ color = myp5.float(1.0); // white for both conditions true
+ } else {
+ color = myp5.float(0.5); // gray for outer true, inner false
+ }
} else {
- color = myp5.float(0.5); // gray for outer true, inner false
+ color = myp5.float(0.0); // black for outer false
}
- } else {
- color = myp5.float(0.0); // black for outer false
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -555,25 +615,27 @@ suite('WebGPU p5.Shader', function() {
// Keep one direct API test for completeness
test('handle direct StrandsIf API usage', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const conditionValue = myp5.uniformFloat(() => 1.0); // true condition
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.5); // initial gray
- const assignments = myp5.strandsIf(
- conditionValue.greaterThan(0),
- () => {
- let tmp = color.copy();
- tmp = myp5.float(1.0); // set to white in if branch
- return { color: tmp };
- }
- ).Else(() => {
- return { color: color }; // keep original in else branch
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const conditionValue = myp5.uniformFloat(() => 1.0); // true condition
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.5); // initial gray
+ const assignments = myp5
+ .strandsIf(conditionValue.greaterThan(0), () => {
+ let tmp = color.copy();
+ tmp = myp5.float(1.0); // set to white in if branch
+ return { color: tmp };
+ })
+ .Else(() => {
+ return { color: color }; // keep original in else branch
+ });
+ color = assignments.color;
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
});
- color = assignments.color;
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -586,34 +648,34 @@ suite('WebGPU p5.Shader', function() {
test('handle direct StrandsIf ElseIf API usage', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const value = myp5.uniformFloat(() => 0.5); // middle value
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0); // initial black
- const assignments = myp5.strandsIf(
- value.greaterThan(0.8),
- () => {
- let tmp = color.copy();
- tmp = myp5.float(1.0); // white for high values
- return { color: tmp };
- }
- ).ElseIf(
- value.greaterThan(0.3),
- () => {
- let tmp = color.copy();
- tmp = myp5.float(0.5); // gray for medium values
- return { color: tmp };
- }
- ).Else(() => {
- let tmp = color.copy();
- tmp = myp5.float(0.0); // black for low values
- return { color: tmp };
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const value = myp5.uniformFloat(() => 0.5); // middle value
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0); // initial black
+ const assignments = myp5
+ .strandsIf(value.greaterThan(0.8), () => {
+ let tmp = color.copy();
+ tmp = myp5.float(1.0); // white for high values
+ return { color: tmp };
+ })
+ .ElseIf(value.greaterThan(0.3), () => {
+ let tmp = color.copy();
+ tmp = myp5.float(0.5); // gray for medium values
+ return { color: tmp };
+ })
+ .Else(() => {
+ let tmp = color.copy();
+ tmp = myp5.float(0.0); // black for low values
+ return { color: tmp };
+ });
+ color = assignments.color;
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
});
- color = assignments.color;
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -627,22 +689,25 @@ suite('WebGPU p5.Shader', function() {
test('using boolean intermediate variables', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- let value = 1;
- let condition = 1 > 2;
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ let value = 1;
+ let condition = 1 > 2;
- if (value < 0.5) {
- condition = 0.5 < 2;
- }
+ if (value < 0.5) {
+ condition = 0.5 < 2;
+ }
- if (condition) {
- return [1, 0, 0, 1]
- }
+ if (condition) {
+ return [1, 0, 0, 1];
+ }
- return [0.4, 0, 0, 1];
- });
- }, { myp5 });
+ return [0.4, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -656,23 +721,26 @@ suite('WebGPU p5.Shader', function() {
test('using boolean intermediate variables in functions', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseFilterShader().modify(() => {
- const conditionMet = () => {
- let condition = 1 > 2;
- let value = 1;
- if (value < 0.5) {
- condition = 0.5 < 2;
- }
- return !condition
- }
- myp5.getColor((inputs, canvasContent) => {
- if (conditionMet()) {
- return [1, 0, 0, 1]
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ const conditionMet = () => {
+ let condition = 1 > 2;
+ let value = 1;
+ if (value < 0.5) {
+ condition = 0.5 < 2;
+ }
+ return !condition;
+ };
+ myp5.getColor((inputs, canvasContent) => {
+ if (conditionMet()) {
+ return [1, 0, 0, 1];
+ }
- return [0.4, 0, 0, 1];
- });
- }, { myp5 });
+ return [0.4, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -686,22 +754,25 @@ suite('WebGPU p5.Shader', function() {
test('using boolean intermediate variables in functions with early returns', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseFilterShader().modify(() => {
- const conditionMet = () => {
- let value = 1;
- if (value < 0.5) {
- return true
- }
- return false
- }
- myp5.getColor((inputs, canvasContent) => {
- if (conditionMet()) {
- return [1, 0, 0, 1]
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ const conditionMet = () => {
+ let value = 1;
+ if (value < 0.5) {
+ return true;
+ }
+ return false;
+ };
+ myp5.getColor((inputs, canvasContent) => {
+ if (conditionMet()) {
+ return [1, 0, 0, 1];
+ }
- return [0.4, 0, 0, 1];
- });
- }, { myp5 });
+ return [0.4, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 255, 255);
myp5.filter(testShader);
@@ -716,12 +787,16 @@ suite('WebGPU p5.Shader', function() {
suite('ternary expressions', () => {
test('ternary changes color based on left/right side of canvas', async () => {
await myp5.createCanvas(50, 25, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- inputs.color = inputs.texCoord.x > 0.5 ? [1, 0, 0, 1] : [0, 0, 1, 1];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ inputs.color =
+ inputs.texCoord.x > 0.5 ? [1, 0, 0, 1] : [0, 0, 1, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -739,13 +814,16 @@ suite('WebGPU p5.Shader', function() {
test('ternary with scalar values', async () => {
await myp5.createCanvas(50, 25, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- const brightness = inputs.texCoord.x > 0.5 ? 1.0 : 0.0;
- inputs.color = [brightness, brightness, brightness, 1];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ const brightness = inputs.texCoord.x > 0.5 ? 1.0 : 0.0;
+ inputs.color = [brightness, brightness, brightness, 1];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
myp5.plane(myp5.width, myp5.height);
@@ -766,18 +844,21 @@ suite('WebGPU p5.Shader', function() {
test('handle simple for loop with known iteration count', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let color = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let color = myp5.float(0.0);
- for (let i = 0; i < 3; i++) {
- color = color + 0.1;
- }
+ for (let i = 0; i < 3; i++) {
+ color = color + 0.1;
+ }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -793,18 +874,21 @@ suite('WebGPU p5.Shader', function() {
test('handle swizzle assignments in loops', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let color = [0, 0, 0, 1];
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let color = [0, 0, 0, 1];
- for (let i = 0; i < 3; i++) {
- color.rgb += 0.1;
- }
+ for (let i = 0; i < 3; i++) {
+ color.rgb += 0.1;
+ }
- inputs.color = color;
- return inputs;
- });
- }, { myp5 });
+ inputs.color = color;
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -820,20 +904,23 @@ suite('WebGPU p5.Shader', function() {
test('handle for loop with variable as loop bound', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- const maxIterations = myp5.uniformInt(() => 2);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ const maxIterations = myp5.uniformInt(() => 2);
- myp5.getPixelInputs(inputs => {
- let result = myp5.float(0.0);
+ myp5.getPixelInputs(inputs => {
+ let result = myp5.float(0.0);
- for (let i = 0; i < maxIterations; i++) {
- result = result + 0.25;
- }
+ for (let i = 0; i < maxIterations; i++) {
+ result = result + 0.25;
+ }
- inputs.color = [result, result, result, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [result, result, result, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -849,20 +936,23 @@ suite('WebGPU p5.Shader', function() {
test('handle for loop modifying multiple variables', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let red = myp5.float(0.0);
- let green = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let red = myp5.float(0.0);
+ let green = myp5.float(0.0);
- for (let i = 0; i < 4; i++) {
- red = red + 0.125; // 4 * 0.125 = 0.5
- green = green + 0.25; // 4 * 0.25 = 1.0
- }
+ for (let i = 0; i < 4; i++) {
+ red = red + 0.125; // 4 * 0.125 = 0.5
+ green = green + 0.25; // 4 * 0.25 = 1.0
+ }
- inputs.color = [red, green, 0.0, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [red, green, 0.0, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -871,26 +961,29 @@ suite('WebGPU p5.Shader', function() {
const pixelColor = await myp5.get(25, 25);
assert.approximately(pixelColor[0], 127, 5); // 0.5 * 255 ≈ 127
assert.approximately(pixelColor[1], 255, 5); // 1.0 * 255 = 255
- assert.approximately(pixelColor[2], 0, 5); // 0.0 * 255 = 0
+ assert.approximately(pixelColor[2], 0, 5); // 0.0 * 255 = 0
});
test('handle for loop with conditional inside', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let sum = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let sum = myp5.float(0.0);
- for (let i = 0; i < 5; i++) {
- if (i % 2 === 0) {
- sum = sum + 0.1; // Add on even iterations: 0, 2, 4
+ for (let i = 0; i < 5; i++) {
+ if (i % 2 === 0) {
+ sum = sum + 0.1; // Add on even iterations: 0, 2, 4
+ }
}
- }
- inputs.color = [sum, sum, sum, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [sum, sum, sum, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -906,20 +999,23 @@ suite('WebGPU p5.Shader', function() {
test('handle nested for loops', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let total = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let total = myp5.float(0.0);
- for (let i = 0; i < 2; i++) {
- for (let j = 0; j < 3; j++) {
- total = total + 0.05; // 2 * 3 = 6 iterations
+ for (let i = 0; i < 2; i++) {
+ for (let j = 0; j < 3; j++) {
+ total = total + 0.05; // 2 * 3 = 6 iterations
+ }
}
- }
- inputs.color = [total, total, total, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [total, total, total, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -935,35 +1031,38 @@ suite('WebGPU p5.Shader', function() {
test('handle complex nested for loops with multiple phi assignments', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let outerSum = myp5.float(0.0);
- let globalCounter = myp5.float(0.0);
-
- // Outer for loop modifying multiple variables
- for (let i = 0; i < 2; i++) {
- let innerSum = myp5.float(0.0);
- let localCounter = myp5.float(0.0);
-
- // Inner for loop also modifying multiple variables
- for (let j = 0; j < 2; j++) {
- innerSum = innerSum + 0.1;
- localCounter = localCounter + 1.0;
- globalCounter = globalCounter + 0.5; // This modifies outer scope
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let outerSum = myp5.float(0.0);
+ let globalCounter = myp5.float(0.0);
+
+ // Outer for loop modifying multiple variables
+ for (let i = 0; i < 2; i++) {
+ let innerSum = myp5.float(0.0);
+ let localCounter = myp5.float(0.0);
+
+ // Inner for loop also modifying multiple variables
+ for (let j = 0; j < 2; j++) {
+ innerSum = innerSum + 0.1;
+ localCounter = localCounter + 1.0;
+ globalCounter = globalCounter + 0.5; // This modifies outer scope
+ }
+
+ // Complex state modification between loops involving all variables
+ innerSum = innerSum * localCounter; // 0.2 * 2.0 = 0.4
+ outerSum = outerSum + innerSum; // Add to outer sum
+ globalCounter = globalCounter * 0.5; // Modify global again
}
- // Complex state modification between loops involving all variables
- innerSum = innerSum * localCounter; // 0.2 * 2.0 = 0.4
- outerSum = outerSum + innerSum; // Add to outer sum
- globalCounter = globalCounter * 0.5; // Modify global again
- }
-
- // Final result should be: 2 iterations * 0.4 = 0.8 for outerSum
- // globalCounter: ((0 + 2*0.5)*0.5 + 2*0.5)*0.5 = ((1)*0.5 + 1)*0.5 = 1.5*0.5 = 0.75
- inputs.color = [outerSum, globalCounter, 0.0, 1.0];
- return inputs;
- });
- }, { myp5 });
+ // Final result should be: 2 iterations * 0.4 = 0.8 for outerSum
+ // globalCounter: ((0 + 2*0.5)*0.5 + 2*0.5)*0.5 = ((1)*0.5 + 1)*0.5 = 1.5*0.5 = 0.75
+ inputs.color = [outerSum, globalCounter, 0.0, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -978,28 +1077,31 @@ suite('WebGPU p5.Shader', function() {
test('handle nested for loops with state modification between loops', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let total = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let total = myp5.float(0.0);
- // Outer for loop
- for (let i = 0; i < 2; i++) {
- let innerSum = myp5.float(0.0);
+ // Outer for loop
+ for (let i = 0; i < 2; i++) {
+ let innerSum = myp5.float(0.0);
- // Inner for loop
- for (let j = 0; j < 3; j++) {
- innerSum = innerSum + 0.1; // 3 * 0.1 = 0.3 per outer iteration
- }
+ // Inner for loop
+ for (let j = 0; j < 3; j++) {
+ innerSum = innerSum + 0.1; // 3 * 0.1 = 0.3 per outer iteration
+ }
- // State modification between inner and outer loop
- innerSum = innerSum * 0.5; // Multiply by 0.5: 0.3 * 0.5 = 0.15
- total = total + innerSum; // Add to total: 2 * 0.15 = 0.3
- }
+ // State modification between inner and outer loop
+ innerSum = innerSum * 0.5; // Multiply by 0.5: 0.3 * 0.5 = 0.15
+ total = total + innerSum; // Add to total: 2 * 0.15 = 0.3
+ }
- inputs.color = [total, total, total, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [total, total, total, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -1015,18 +1117,21 @@ suite('WebGPU p5.Shader', function() {
test('handle for loop using loop variable in calculations', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let sum = myp5.float(0.0);
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let sum = myp5.float(0.0);
- for (let i = 1; i <= 3; i++) {
- sum = sum + (i * 0.1); // 1*0.1 + 2*0.1 + 3*0.1 = 0.6
- }
+ for (let i = 1; i <= 3; i++) {
+ sum = sum + i * 0.1; // 1*0.1 + 2*0.1 + 3*0.1 = 0.6
+ }
- inputs.color = [sum, sum, sum, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [sum, sum, sum, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -1043,27 +1148,30 @@ suite('WebGPU p5.Shader', function() {
test('handle direct StrandsFor API usage', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let accumulator = myp5.float(0.0);
-
- const loopResult = myp5.strandsFor(
- () => 0,
- (loopVar) => loopVar < 4,
- (loopVar) => loopVar + 1,
- (loopVar, vars) => {
- let newValue = vars.accumulator.copy();
- newValue = newValue + 0.125;
- return { accumulator: newValue };
- },
- { accumulator: accumulator.copy() },
- );
-
- accumulator = loopResult.accumulator;
- inputs.color = [accumulator, accumulator, accumulator, 1.0];
- return inputs;
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let accumulator = myp5.float(0.0);
+
+ const loopResult = myp5.strandsFor(
+ () => 0,
+ loopVar => loopVar < 4,
+ loopVar => loopVar + 1,
+ (loopVar, vars) => {
+ let newValue = vars.accumulator.copy();
+ newValue = newValue + 0.125;
+ return { accumulator: newValue };
+ },
+ { accumulator: accumulator.copy() }
+ );
+
+ accumulator = loopResult.accumulator;
+ inputs.color = [accumulator, accumulator, accumulator, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -1079,22 +1187,25 @@ suite('WebGPU p5.Shader', function() {
test('handle for loop with break statement', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseMaterialShader().modify(() => {
- myp5.getPixelInputs(inputs => {
- let color = 0;
- let maxIterations = 5;
-
- for (let i = 0; i < 100; i++) {
- if (i >= maxIterations) {
- break;
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getPixelInputs(inputs => {
+ let color = 0;
+ let maxIterations = 5;
+
+ for (let i = 0; i < 100; i++) {
+ if (i >= maxIterations) {
+ break;
+ }
+ color = color + 0.1;
}
- color = color + 0.1;
- }
- inputs.color = [color, color, color, 1.0];
- return inputs;
- });
- }, { myp5 });
+ inputs.color = [color, color, color, 1.0];
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
myp5.noStroke();
myp5.shader(testShader);
@@ -1111,16 +1222,19 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.varyingVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor((c) => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255); // Make the background blue to tell it apart
myp5.noStroke();
@@ -1129,13 +1243,19 @@ suite('WebGPU p5.Shader', function() {
// The middle should have position 0,0 which translates to black
await myp5.loadPixels();
- const midColor = myp5.pixels.slice((25 * myp5.width + 25) * 4, (25 * myp5.width + 25) * 4 + 4);
+ const midColor = myp5.pixels.slice(
+ (25 * myp5.width + 25) * 4,
+ (25 * myp5.width + 25) * 4 + 4
+ );
assert.approximately(midColor[0], 0, 5);
assert.approximately(midColor[1], 0, 5);
assert.approximately(midColor[2], 0, 5);
// The corner should have position 1,1 which translates to yellow
- const cornerColor = myp5.pixels.slice((0 * myp5.width + 0) * 4, (0 * myp5.width + 0) * 4 + 4);
+ const cornerColor = myp5.pixels.slice(
+ (0 * myp5.width + 0) * 4,
+ (0 * myp5.width + 0) * 4 + 4
+ );
assert.approximately(cornerColor[0], 255, 5);
assert.approximately(cornerColor[1], 255, 5);
assert.approximately(cornerColor[2], 0, 5);
@@ -1145,16 +1265,19 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.varyingVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos.xyz = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor((c) => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos.xyz = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255); // Make the background blue to tell it apart
myp5.noStroke();
@@ -1163,13 +1286,19 @@ suite('WebGPU p5.Shader', function() {
await myp5.loadPixels();
// The middle should have position 0,0 which translates to black
- const midColor = await myp5.pixels.slice((25 * myp5.width + 25) * 4, (25 * myp5.width + 25) * 4 + 4);
+ const midColor = await myp5.pixels.slice(
+ (25 * myp5.width + 25) * 4,
+ (25 * myp5.width + 25) * 4 + 4
+ );
assert.approximately(midColor[0], 0, 5);
assert.approximately(midColor[1], 0, 5);
assert.approximately(midColor[2], 0, 5);
// The corner should have position 1,1 which translates to yellow
- const cornerColor = await myp5.pixels.slice((0 * myp5.width + 0) * 4, (0 * myp5.width + 0) * 4 + 4);
+ const cornerColor = await myp5.pixels.slice(
+ (0 * myp5.width + 0) * 4,
+ (0 * myp5.width + 0) * 4 + 4
+ );
assert.approximately(cornerColor[0], 255, 5);
assert.approximately(cornerColor[1], 255, 5);
assert.approximately(cornerColor[2], 0, 5);
@@ -1179,17 +1308,20 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using implicit varying name with two underscores, which should be remapped
- let __worldPos = myp5.varyingVec3();
- myp5.getWorldInputs(inputs => {
- __worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(__worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using implicit varying name with two underscores, which should be remapped
+ let __worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ __worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(__worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255);
myp5.noStroke();
@@ -1211,17 +1343,20 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- // using explicit varying name with two underscores, which should be remapped
- let worldPos = myp5.varyingVec3('__worldPos');
- myp5.getWorldInputs(inputs => {
- worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ // using explicit varying name with two underscores, which should be remapped
+ let worldPos = myp5.varyingVec3('__worldPos');
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255);
myp5.noStroke();
@@ -1243,17 +1378,20 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.varyingVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos = inputs.position.xyz;
- inputs.position.xyz = worldPos + [25, 25, 0];
- return inputs;
- });
- myp5.getFinalColor((c) => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.varyingVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ inputs.position.xyz = worldPos + [25, 25, 0];
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255); // Make the background blue to tell it apart
myp5.noStroke();
@@ -1262,13 +1400,19 @@ suite('WebGPU p5.Shader', function() {
await myp5.loadPixels();
// The middle (shifted +25,25) should have position 0,0 which translates to black
- const midColor = myp5.pixels.slice((49 * myp5.width + 49) * 4, (49 * myp5.width + 49) * 4 + 4);
+ const midColor = myp5.pixels.slice(
+ (49 * myp5.width + 49) * 4,
+ (49 * myp5.width + 49) * 4 + 4
+ );
assert.approximately(midColor[0], 0, 5);
assert.approximately(midColor[1], 0, 5);
assert.approximately(midColor[2], 0, 5);
// The corner (shifted +25,25) should have position 1,1 which translates to yellow
- const cornerColor = myp5.pixels.slice((25 * myp5.width + 25) * 4, (25 * myp5.width + 25) * 4 + 4);
+ const cornerColor = myp5.pixels.slice(
+ (25 * myp5.width + 25) * 4,
+ (25 * myp5.width + 25) * 4 + 4
+ );
assert.approximately(cornerColor[0], 255, 5);
assert.approximately(cornerColor[1], 255, 5);
assert.approximately(cornerColor[2], 0, 5);
@@ -1278,17 +1422,20 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let processedNormal = myp5.sharedVec3();
- myp5.getPixelInputs((inputs) => {
- processedNormal = myp5.normalize(inputs.normal);
- return inputs;
- });
- myp5.getFinalColor((c) => {
- // Use the processed normal to create a color - should be [0, 0, 1] for plane facing camera
- return [myp5.abs(processedNormal), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let processedNormal = myp5.sharedVec3();
+ myp5.getPixelInputs(inputs => {
+ processedNormal = myp5.normalize(inputs.normal);
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ // Use the processed normal to create a color - should be [0, 0, 1] for plane facing camera
+ return [myp5.abs(processedNormal), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0); // Red background to distinguish from result
myp5.noStroke();
@@ -1297,8 +1444,8 @@ suite('WebGPU p5.Shader', function() {
// Normal of plane facing camera should be [0, 0, 1], so color should be [0, 0, 255]
const centerColor = await myp5.get(25, 25);
- assert.approximately(centerColor[0], 0, 5); // Red component
- assert.approximately(centerColor[1], 0, 5); // Green component
+ assert.approximately(centerColor[0], 0, 5); // Red component
+ assert.approximately(centerColor[1], 0, 5); // Green component
assert.approximately(centerColor[2], 255, 5); // Blue component
});
@@ -1306,21 +1453,24 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let processedNormal = myp5.sharedVec3();
- myp5.objectInputs.begin();
- myp5.objectInputs.position += [0, 0, 0];
- myp5.objectInputs.end();
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let processedNormal = myp5.sharedVec3();
+ myp5.objectInputs.begin();
+ myp5.objectInputs.position += [0, 0, 0];
+ myp5.objectInputs.end();
- myp5.pixelInputs.begin();
- processedNormal = myp5.normalize(myp5.pixelInputs.normal);
- myp5.pixelInputs.end();
+ myp5.pixelInputs.begin();
+ processedNormal = myp5.normalize(myp5.pixelInputs.normal);
+ myp5.pixelInputs.end();
- myp5.finalColor.begin();
- // Use the processed normal to create a color - should be [0, 0, 1] for plane facing camera
- myp5.finalColor.set([myp5.abs(processedNormal), 1]);
- myp5.finalColor.end();
- }, { myp5 });
+ myp5.finalColor.begin();
+ // Use the processed normal to create a color - should be [0, 0, 1] for plane facing camera
+ myp5.finalColor.set([myp5.abs(processedNormal), 1]);
+ myp5.finalColor.end();
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0); // Red background to distinguish from result
myp5.noStroke();
@@ -1329,8 +1479,8 @@ suite('WebGPU p5.Shader', function() {
// Normal of plane facing camera should be [0, 0, 1], so color should be [0, 0, 255]
const centerColor = await myp5.get(25, 25);
- assert.approximately(centerColor[0], 0, 5); // Red component
- assert.approximately(centerColor[1], 0, 5); // Green component
+ assert.approximately(centerColor[0], 0, 5); // Red component
+ assert.approximately(centerColor[1], 0, 5); // Green component
assert.approximately(centerColor[2], 255, 5); // Blue component
});
@@ -1338,16 +1488,19 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let __processedNormal = myp5.sharedVec3();
- myp5.getPixelInputs(inputs => {
- __processedNormal = myp5.normalize(inputs.normal);
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(__processedNormal), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let __processedNormal = myp5.sharedVec3();
+ myp5.getPixelInputs(inputs => {
+ __processedNormal = myp5.normalize(inputs.normal);
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(__processedNormal), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0);
myp5.noStroke();
@@ -1364,16 +1517,19 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let processedNormal = myp5.sharedVec3('__processedNormal');
- myp5.getPixelInputs(inputs => {
- processedNormal = myp5.normalize(inputs.normal);
- return inputs;
- });
- myp5.getFinalColor(() => {
- return [myp5.abs(processedNormal), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let processedNormal = myp5.sharedVec3('__processedNormal');
+ myp5.getPixelInputs(inputs => {
+ processedNormal = myp5.normalize(inputs.normal);
+ return inputs;
+ });
+ myp5.getFinalColor(() => {
+ return [myp5.abs(processedNormal), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(255, 0, 0);
myp5.noStroke();
@@ -1390,16 +1546,19 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
myp5.pixelDensity(1);
- const testShader = myp5.baseMaterialShader().modify(() => {
- let worldPos = myp5.sharedVec3();
- myp5.getWorldInputs((inputs) => {
- worldPos = inputs.position.xyz;
- return inputs;
- });
- myp5.getFinalColor((c) => {
- return [myp5.abs(worldPos / 25), 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseMaterialShader().modify(
+ () => {
+ let worldPos = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPos = inputs.position.xyz;
+ return inputs;
+ });
+ myp5.getFinalColor(c => {
+ return [myp5.abs(worldPos / 25), 1];
+ });
+ },
+ { myp5 }
+ );
myp5.background(0, 0, 255); // Make the background blue to tell it apart
myp5.noStroke();
@@ -1408,13 +1567,19 @@ suite('WebGPU p5.Shader', function() {
await myp5.loadPixels();
// The middle should have position 0,0 which translates to black
- const midColor = myp5.pixels.slice((25 * myp5.width + 25) * 4, (25 * myp5.width + 25) * 4 + 4);
+ const midColor = myp5.pixels.slice(
+ (25 * myp5.width + 25) * 4,
+ (25 * myp5.width + 25) * 4 + 4
+ );
assert.approximately(midColor[0], 0, 5);
assert.approximately(midColor[1], 0, 5);
assert.approximately(midColor[2], 0, 5);
// The corner should have position 1,1 which translates to yellow
- const cornerColor = myp5.pixels.slice((0 * myp5.width + 0) * 4, (0 * myp5.width + 0) * 4 + 4);
+ const cornerColor = myp5.pixels.slice(
+ (0 * myp5.width + 0) * 4,
+ (0 * myp5.width + 0) * 4 + 4
+ );
assert.approximately(cornerColor[0], 255, 5);
assert.approximately(cornerColor[1], 255, 5);
assert.approximately(cornerColor[2], 0, 5);
@@ -1425,12 +1590,15 @@ suite('WebGPU p5.Shader', function() {
test('handle getColor hook with non-struct return type', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- // Simple test - just return a constant color
- return [1.0, 0.5, 0.0, 1.0]; // Orange color
- });
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ // Simple test - just return a constant color
+ return [1.0, 0.5, 0.0, 1.0]; // Orange color
+ });
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(0, 0, 255); // Blue background
@@ -1442,43 +1610,54 @@ suite('WebGPU p5.Shader', function() {
const pixelColor = await myp5.get(25, 25);
assert.approximately(pixelColor[0], 255, 5); // Red channel should be 255
assert.approximately(pixelColor[1], 127, 5); // Green channel should be ~127
- assert.approximately(pixelColor[2], 0, 5); // Blue channel should be 0
+ assert.approximately(pixelColor[2], 0, 5); // Blue channel should be 0
});
test('simple vector multiplication in filter shader', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor((inputs, canvasContent) => {
- // Test simple scalar * vector operation
- const scalar = 0.5;
- const vector = [1, 2];
- const result = scalar * vector;
- return [result.x, result.y, 0, 1];
- });
- }, { myp5 });
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor((inputs, canvasContent) => {
+ // Test simple scalar * vector operation
+ const scalar = 0.5;
+ const vector = [1, 2];
+ const result = scalar * vector;
+ return [result.x, result.y, 0, 1];
+ });
+ },
+ { myp5 }
+ );
});
test('handle complex filter shader with for loop and vector operations', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const testShader = myp5.baseFilterShader().modify(() => {
- const r = myp5.uniformFloat(() => 3); // Small value for testing
- myp5.getColor((inputs, canvasContent) => {
- let sum = [0, 0, 0, 0];
- let samples = 1;
-
- for (let i = 0; i < r; i++) {
- samples++;
- sum += myp5.texture(canvasContent, inputs.texCoord + (i / r) * [
- myp5.sin(4 * myp5.PI * i / r),
- myp5.cos(4 * myp5.PI * i / r)
- ]);
- }
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ const r = myp5.uniformFloat(() => 3); // Small value for testing
+ myp5.getColor((inputs, canvasContent) => {
+ let sum = [0, 0, 0, 0];
+ let samples = 1;
+
+ for (let i = 0; i < r; i++) {
+ samples++;
+ sum += myp5.texture(
+ canvasContent,
+ inputs.texCoord +
+ (i / r) *
+ [
+ myp5.sin((4 * myp5.PI * i) / r),
+ myp5.cos((4 * myp5.PI * i) / r)
+ ]
+ );
+ }
- return sum / samples;
- });
- }, { myp5 });
+ return sum / samples;
+ });
+ },
+ { myp5 }
+ );
// Create a simple scene to filter
myp5.background(255, 0, 0); // Red background
@@ -1499,29 +1678,39 @@ suite('WebGPU p5.Shader', function() {
test('noiseDetail state does not leak across modify calls', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const firstShader = myp5.baseFilterShader().modify(() => {
- myp5.noiseDetail(7, 0.2);
- myp5.getColor(() => {
- return [myp5.noise(10), 0, 0, 1];
- });
- }, { myp5 });
-
- const secondShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(10), 0, 0, 1];
- });
- }, { myp5 });
-
- const explicitDefaultShader = myp5.baseFilterShader().modify(() => {
- myp5.noiseDetail(4, 0.5);
- myp5.getColor(() => {
- return [myp5.noise(10), 0, 0, 1];
- });
- }, { myp5 });
+ const firstShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.noiseDetail(7, 0.2);
+ myp5.getColor(() => {
+ return [myp5.noise(10), 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
+
+ const secondShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(10), 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
+
+ const explicitDefaultShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.noiseDetail(4, 0.5);
+ myp5.getColor(() => {
+ return [myp5.noise(10), 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
const firstHook = firstShader.hooks.fragment['vec4 getColor'];
const secondHook = secondShader.hooks.fragment['vec4 getColor'];
- const defaultHook = explicitDefaultShader.hooks.fragment['vec4 getColor'];
+ const defaultHook =
+ explicitDefaultShader.hooks.fragment['vec4 getColor'];
assert.notStrictEqual(firstHook, secondHook);
assert.strictEqual(secondHook, defaultHook);
@@ -1529,61 +1718,81 @@ suite('WebGPU p5.Shader', function() {
for (let i = 1; i <= 3; i++) {
test(`works with ${i}D vectors`, async () => {
- await expect((async () => {
- await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const input = new Array(i).fill(10);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(input), 0, 0, 1];
- });
- }, { myp5, input });
- myp5.shader(testShader);
- myp5.plane(10, 10);
- })()).resolves.not.toThrowError();
+ await expect(
+ (async () => {
+ await myp5.createCanvas(50, 50, myp5.WEBGPU);
+ const input = new Array(i).fill(10);
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(input), 0, 0, 1];
+ });
+ },
+ { myp5, input }
+ );
+ myp5.shader(testShader);
+ myp5.plane(10, 10);
+ })()
+ ).resolves.not.toThrowError();
});
test(`works with ${i}D positional arguments`, async () => {
- await expect((async () => {
- await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const input = new Array(i).fill(10);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(...input), 0, 0, 1];
- });
- }, { myp5, input });
- myp5.shader(testShader);
- myp5.plane(10, 10);
- })()).resolves.not.toThrowError();
+ await expect(
+ (async () => {
+ await myp5.createCanvas(50, 50, myp5.WEBGPU);
+ const input = new Array(i).fill(10);
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(...input), 0, 0, 1];
+ });
+ },
+ { myp5, input }
+ );
+ myp5.shader(testShader);
+ myp5.plane(10, 10);
+ })()
+ ).resolves.not.toThrowError();
});
}
for (const i of [0, 4]) {
test(`Does not work in ${i}D`, async () => {
- await expect((async () => {
- await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const input = new Array(i).fill(10);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(input), 0, 0, 1];
- });
- }, { myp5, input });
- myp5.shader(testShader);
- myp5.plane(10, 10);
- })()).rejects.toThrowError();
+ await expect(
+ (async () => {
+ await myp5.createCanvas(50, 50, myp5.WEBGPU);
+ const input = new Array(i).fill(10);
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(input), 0, 0, 1];
+ });
+ },
+ { myp5, input }
+ );
+ myp5.shader(testShader);
+ myp5.plane(10, 10);
+ })()
+ ).rejects.toThrowError();
});
test(`Does not work in ${i}D with positional arguments`, async () => {
- await expect((async () => {
- await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const input = new Array(i).fill(10);
- const testShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => {
- return [myp5.noise(...input), 0, 0, 1];
- });
- }, { myp5, input });
- myp5.shader(testShader);
- myp5.plane(10, 10);
- })()).rejects.toThrowError();
+ await expect(
+ (async () => {
+ await myp5.createCanvas(50, 50, myp5.WEBGPU);
+ const input = new Array(i).fill(10);
+ const testShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => {
+ return [myp5.noise(...input), 0, 0, 1];
+ });
+ },
+ { myp5, input }
+ );
+ myp5.shader(testShader);
+ myp5.plane(10, 10);
+ })()
+ ).rejects.toThrowError();
});
}
});
@@ -1591,32 +1800,50 @@ suite('WebGPU p5.Shader', function() {
test('randomSeed state does not leak across modify calls', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const firstShader = myp5.baseFilterShader().modify(() => {
- myp5.randomSeed(12);
- myp5.getColor(() => [myp5.random(), 0, 0, 1]);
- }, { myp5 });
+ const firstShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.randomSeed(12);
+ myp5.getColor(() => [myp5.random(), 0, 0, 1]);
+ },
+ { myp5 }
+ );
- const secondShader = myp5.baseFilterShader().modify(() => {
- myp5.getColor(() => [myp5.random(), 0, 0, 1]);
- }, { myp5 });
+ const secondShader = myp5.baseFilterShader().modify(
+ () => {
+ myp5.getColor(() => [myp5.random(), 0, 0, 1]);
+ },
+ { myp5 }
+ );
- assert.strictEqual(firstShader.hooks.uniforms['_p5_randomSeed: f32'](), 12);
- assert.notStrictEqual(secondShader.hooks.uniforms['_p5_randomSeed: f32'](), 12);
+ assert.strictEqual(
+ firstShader.hooks.uniforms['_p5_randomSeed: f32'](),
+ 12
+ );
+ assert.notStrictEqual(
+ secondShader.hooks.uniforms['_p5_randomSeed: f32'](),
+ 12
+ );
});
test('instanceID varying does not leak across modify calls', async () => {
await myp5.createCanvas(50, 50, myp5.WEBGPU);
- const firstShader = myp5.baseMaterialShader().modify(() => {
- myp5.getFinalColor(() => {
- const id = myp5.instanceID();
- return [id / 10, 0, 0, 1];
- });
- }, { myp5 });
+ const firstShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getFinalColor(() => {
+ const id = myp5.instanceID();
+ return [id / 10, 0, 0, 1];
+ });
+ },
+ { myp5 }
+ );
- const secondShader = myp5.baseMaterialShader().modify(() => {
- myp5.getFinalColor(() => [1, 0, 0, 1]);
- }, { myp5 });
+ const secondShader = myp5.baseMaterialShader().modify(
+ () => {
+ myp5.getFinalColor(() => [1, 0, 0, 1]);
+ },
+ { myp5 }
+ );
assert.isDefined(firstShader.hooks.instanceIDVarying);
assert.isNull(secondShader.hooks.instanceIDVarying);
@@ -1630,12 +1857,15 @@ suite('WebGPU p5.Shader', function() {
// correctly handle void hooks with early returns without crashing
// the strands compiler or hitting type errors.
expect(() => {
- const computeShader = myp5.buildComputeShader(() => {
- const id = myp5.index.x;
- if (id > 10) {
- return; // Early return in void hook
- }
- }, { myp5 });
+ const computeShader = myp5.buildComputeShader(
+ () => {
+ const id = myp5.index.x;
+ if (id > 10) {
+ return; // Early return in void hook
+ }
+ },
+ { myp5 }
+ );
myp5.compute(computeShader, 1);
}).not.toThrow();
@@ -1645,15 +1875,18 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
const data = myp5.createStorage([0]);
- const computeShader = myp5.buildComputeShader(() => {
- const buf = myp5.uniformStorage();
- const id = myp5.index.x;
- if (id == 0) {
- buf[0] = 1.0;
- return;
- buf[0] = 2.0; // Should not execute
- }
- }, { myp5 });
+ const computeShader = myp5.buildComputeShader(
+ () => {
+ const buf = myp5.uniformStorage();
+ const id = myp5.index.x;
+ if (id == 0) {
+ buf[0] = 1.0;
+ return;
+ buf[0] = 2.0; // Should not execute
+ }
+ },
+ { myp5 }
+ );
computeShader.setUniform('buf', data);
@@ -1667,12 +1900,15 @@ suite('WebGPU p5.Shader', function() {
const input = new Float32Array([1, 2, 3, 4]);
const buf = myp5.createStorage(input);
- const computeShader = myp5.buildComputeShader(() => {
- // using implicit uniform storage name with two underscores, which should be remapped
- const __data = myp5.uniformStorage();
- const idx = myp5.index.x;
- __data[idx] = __data[idx] * 2;
- }, { myp5 });
+ const computeShader = myp5.buildComputeShader(
+ () => {
+ // using implicit uniform storage name with two underscores, which should be remapped
+ const __data = myp5.uniformStorage();
+ const idx = myp5.index.x;
+ __data[idx] = __data[idx] * 2;
+ },
+ { myp5 }
+ );
// setting the uniform using the original name
computeShader.setUniform('__data', buf);
@@ -1689,12 +1925,15 @@ suite('WebGPU p5.Shader', function() {
const input = new Float32Array([1, 2, 3, 4]);
const buf = myp5.createStorage(input);
- const computeShader = myp5.buildComputeShader(() => {
- // using explicit uniform storage name with two underscores, which should be remapped
- const data = myp5.uniformStorage('__data');
- const idx = myp5.index.x;
- data[idx] = data[idx] * 2;
- }, { myp5 });
+ const computeShader = myp5.buildComputeShader(
+ () => {
+ // using explicit uniform storage name with two underscores, which should be remapped
+ const data = myp5.uniformStorage('__data');
+ const idx = myp5.index.x;
+ data[idx] = data[idx] * 2;
+ },
+ { myp5 }
+ );
// setting the uniform using the original name
computeShader.setUniform('__data', buf);
@@ -1712,14 +1951,17 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
const storage = myp5.createStorage(new Float32Array(4));
- const computeShader = myp5.buildComputeShader(() => {
- const data = myp5.uniformStorage();
- function getArray() {
- return [1, 2];
- }
- const arr = getArray();
- data[myp5.index.x] = arr[0] + arr[1] + myp5.index.x;
- }, { myp5 });
+ const computeShader = myp5.buildComputeShader(
+ () => {
+ const data = myp5.uniformStorage();
+ function getArray() {
+ return [1, 2];
+ }
+ const arr = getArray();
+ data[myp5.index.x] = arr[0] + arr[1] + myp5.index.x;
+ },
+ { myp5 }
+ );
computeShader.setUniform('data', storage);
@@ -1732,10 +1974,13 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
const storage = myp5.createStorage(new Float32Array(4));
- const computeShader = myp5.buildComputeShader(() => {
- const data = myp5.uniformStorage();
- data[myp5.index.x] = [1, 2][0];
- }, { myp5 });
+ const computeShader = myp5.buildComputeShader(
+ () => {
+ const data = myp5.uniformStorage();
+ data[myp5.index.x] = [1, 2][0];
+ },
+ { myp5 }
+ );
computeShader.setUniform('data', storage);
@@ -1748,11 +1993,14 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
expect(() => {
- myp5.buildComputeShader(() => {
- const data = myp5.uniformStorage();
- const arr = [1];
- data[myp5.index.x] = arr[0];
- }, { myp5 });
+ myp5.buildComputeShader(
+ () => {
+ const data = myp5.uniformStorage();
+ const arr = [1];
+ data[myp5.index.x] = arr[0];
+ },
+ { myp5 }
+ );
}).toThrow('and must have 2-4 elements (got 1)');
});
@@ -1760,11 +2008,14 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
expect(() => {
- myp5.buildComputeShader(() => {
- const data = myp5.uniformStorage();
- const arr = [1, 2, 3, 4, 5];
- data[myp5.index.x] = arr[0];
- }, { myp5 });
+ myp5.buildComputeShader(
+ () => {
+ const data = myp5.uniformStorage();
+ const arr = [1, 2, 3, 4, 5];
+ data[myp5.index.x] = arr[0];
+ },
+ { myp5 }
+ );
}).toThrow('and must have 2-4 elements (got 5)');
});
@@ -1773,27 +2024,36 @@ suite('WebGPU p5.Shader', function() {
const storage = myp5.createStorage(new Float32Array(4));
expect(() => {
- const s2 = myp5.buildComputeShader(() => {
- const data = myp5.uniformStorage();
- const arr = [1, 2];
- data[myp5.index.x] = arr[0];
- }, { myp5 });
+ const s2 = myp5.buildComputeShader(
+ () => {
+ const data = myp5.uniformStorage();
+ const arr = [1, 2];
+ data[myp5.index.x] = arr[0];
+ },
+ { myp5 }
+ );
s2.setUniform('data', storage);
myp5.compute(s2, 4);
- const s3 = myp5.buildComputeShader(() => {
- const data = myp5.uniformStorage();
- const arr = [1, 2, 3];
- data[myp5.index.x] = arr[0];
- }, { myp5 });
+ const s3 = myp5.buildComputeShader(
+ () => {
+ const data = myp5.uniformStorage();
+ const arr = [1, 2, 3];
+ data[myp5.index.x] = arr[0];
+ },
+ { myp5 }
+ );
s3.setUniform('data', storage);
myp5.compute(s3, 4);
- const s4 = myp5.buildComputeShader(() => {
- const data = myp5.uniformStorage();
- const arr = [1, 2, 3, 4];
- data[myp5.index.x] = arr[0];
- }, { myp5 });
+ const s4 = myp5.buildComputeShader(
+ () => {
+ const data = myp5.uniformStorage();
+ const arr = [1, 2, 3, 4];
+ data[myp5.index.x] = arr[0];
+ },
+ { myp5 }
+ );
s4.setUniform('data', storage);
myp5.compute(s4, 4);
}).not.toThrow();
@@ -1803,13 +2063,16 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- let worldPosX = myp5.sharedVec3();
- myp5.getWorldInputs(inputs => {
- worldPosX = inputs.position.x; // scalar → vec3, valid broadcast
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ let worldPosX = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ worldPosX = inputs.position.x; // scalar → vec3, valid broadcast
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
}).not.toThrow();
});
@@ -1817,13 +2080,16 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- let myVec = myp5.sharedVec3();
- myp5.getWorldInputs(inputs => {
- myVec = inputs.position.xy; // vec2 → vec3 mismatch
- return inputs;
- });
- }, { myp5 });
+ myp5.baseMaterialShader().modify(
+ () => {
+ let myVec = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ myVec = inputs.position.xy; // vec2 → vec3 mismatch
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
}).toThrow(/dimension mismatch/);
});
@@ -1831,13 +2097,16 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- let myVec = myp5.sharedVec3();
- myp5.getWorldInputs(inputs => {
- myVec.xy = inputs.position; // vec3 → 2-component swizzle mismatch
- return inputs;
- });
- },{myp5});
+ myp5.baseMaterialShader().modify(
+ () => {
+ let myVec = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ myVec.xy = inputs.position; // vec3 → 2-component swizzle mismatch
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
}).toThrow(/dimension mismatch/);
});
@@ -1845,13 +2114,16 @@ suite('WebGPU p5.Shader', function() {
await myp5.createCanvas(5, 5, myp5.WEBGPU);
expect(() => {
- myp5.baseMaterialShader().modify(() => {
- let myVec = myp5.sharedVec3();
- myp5.getWorldInputs(inputs => {
- myVec = inputs.position; // vec3 → vec3, OK
- return inputs;
- });
- },{myp5});
+ myp5.baseMaterialShader().modify(
+ () => {
+ let myVec = myp5.sharedVec3();
+ myp5.getWorldInputs(inputs => {
+ myVec = inputs.position; // vec3 → vec3, OK
+ return inputs;
+ });
+ },
+ { myp5 }
+ );
}).not.toThrow();
});
});
diff --git a/translations/dev.js b/translations/dev.js
index 08651b2a76..e30da4ed42 100644
--- a/translations/dev.js
+++ b/translations/dev.js
@@ -16,4 +16,4 @@ export { default as ja_translation } from './ja/translation.json';
*
* "es_MX" is the language key whereas "translation" is the filename
* ( translation.json ) or the namespace
-*/
+ */
diff --git a/translations/index.js b/translations/index.js
index a04c06d1d9..591f918248 100644
--- a/translations/index.js
+++ b/translations/index.js
@@ -22,11 +22,4 @@ export default {
* dev.js, which is another file in this folder.
* @private
*/
-export const languages = [
- 'en',
- 'es',
- 'ko',
- 'zh',
- 'hi',
- 'ja'
-];
+export const languages = ['en', 'es', 'ko', 'zh', 'hi', 'ja'];
diff --git a/translations/ko/README.md b/translations/ko/README.md
index c15844116f..aad3b0225a 100644
--- a/translations/ko/README.md
+++ b/translations/ko/README.md
@@ -1,26 +1,29 @@
# Welcome to the FES Korean branch!
+
안녕하세요, FES 한국어 브랜치에 어서오세요!
## 한국어 공동 번역 기여자 Korean Translation Credits
+
2021년 가을부터 공동작업으로 진행되어 2022년 1월에 마무리된 FES 에러메시지 공동 번역 작업은 아래 분들이 함께하셨습니다.
-* [염인화](https://yinhwa.art/) (Inhwa Yeom): artist/XR researcher based in South Korea. (Take a look at her works on [p5 for 50+](https://p5for50.plus/) ([Processing Foundation Fellows 2020](https://medium.com/processing-foundation/p5-js-for-ages-50-in-korea-50d47b5927fb)) and p5js website Korean translation)
-* 전유진 (Youjin Jeon): artist/organizer based in Seoul, South Korea. [여성을 위한 열린 기술랩(Woman Open Tech Lab.kr)](http://womanopentechlab.kr/) and [Seoul Express](http://seoulexpress.kr/)
-* [정앎](https://www.almichu.com/) (Alm Chung, organizer): Korean-American artist/researcher based in Seattle, WA.
-* 이지현 (Jihyun Lee): Korean publishing editor based in South Korea
+
+- [염인화](https://yinhwa.art/) (Inhwa Yeom): artist/XR researcher based in South Korea. (Take a look at her works on [p5 for 50+](https://p5for50.plus/) ([Processing Foundation Fellows 2020](https://medium.com/processing-foundation/p5-js-for-ages-50-in-korea-50d47b5927fb)) and p5js website Korean translation)
+- 전유진 (Youjin Jeon): artist/organizer based in Seoul, South Korea. [여성을 위한 열린 기술랩(Woman Open Tech Lab.kr)](http://womanopentechlab.kr/) and [Seoul Express](http://seoulexpress.kr/)
+- [정앎](https://www.almichu.com/) (Alm Chung, organizer): Korean-American artist/researcher based in Seattle, WA.
+- 이지현 (Jihyun Lee): Korean publishing editor based in South Korea
## 영한 번역 리소스 (Korean-English Translation Resources)
-* 영한 [번역에 도움이 되는 툴과 유의점들]입니다.
-* 또한 영한 [번역 작업 중 마주치는 딜레마들] 속에서 저희가 채택한 방식을 모아 적어봤습니다.
-* 외래 [기술 용어 다루기]에 대한 논의입니다.
-* p5js 웹사이트와 기술 문서에서 사용하는 기술 용어들을 통일하기위해 사용하고 있 [p5js.org/ko 기술 용어 색인] 입니다.
-* 현존하는 검색툴/번역 툴들과 연계 가능한 "[사이를 맴도는]" 번역문에 대해 생각해보는 글입니다.
-이 외에도 FES의 세계화 작업 과정, 그리고 과정 중 논의된 이슈들을 [Friendly Errors i18n Book ✎ 친절한 오류 메시지 세계화 가이드북]에서 읽어보실 수 있습니다. "친절한 오류 메시지 세계화 가이드북"은 오픈 소스 프로젝트이며, 이 [독립된 레파지토리 (repository)]를 통해 기여 가능합니다.
+- 영한 [번역에 도움이 되는 툴과 유의점들]입니다.
+- 또한 영한 [번역 작업 중 마주치는 딜레마들] 속에서 저희가 채택한 방식을 모아 적어봤습니다.
+- 외래 [기술 용어 다루기]에 대한 논의입니다.
+- p5js 웹사이트와 기술 문서에서 사용하는 기술 용어들을 통일하기위해 사용하고 있 [p5js.org/ko 기술 용어 색인] 입니다.
+- 현존하는 검색툴/번역 툴들과 연계 가능한 "[사이를 맴도는]" 번역문에 대해 생각해보는 글입니다.
+이 외에도 FES의 세계화 작업 과정, 그리고 과정 중 논의된 이슈들을 [Friendly Errors i18n Book ✎ 친절한 오류 메시지 세계화 가이드북]에서 읽어보실 수 있습니다. "친절한 오류 메시지 세계화 가이드북"은 오픈 소스 프로젝트이며, 이 [독립된 레파지토리 (repository)]를 통해 기여 가능합니다.
[번역에 도움이 되는 툴과 유의점들]: https://almchung.github.io/p5-fes-i18n-book/ch4/#tools
[번역 작업 중 마주치는 딜레마들]: https://almchung.github.io/p5-fes-i18n-book/ch4/#dilemmas
[기술 용어 다루기]: https://almchung.github.io/p5-fes-i18n-book/ch3/
[사이를 맴도는]: https://almchung.github.io/p5-fes-i18n-book/ch5/
[Friendly Errors i18n Book ✎ 친절한 오류 메시지 세계화 가이드북]: https://almchung.github.io/p5-fes-i18n-book/
-[독립된 레파지토리 (repository)]: https://github.com/almchung/p5-fes-i18n-book
\ No newline at end of file
+[독립된 레파지토리 (repository)]: https://github.com/almchung/p5-fes-i18n-book
diff --git a/utils/convert.mjs b/utils/convert.mjs
index 9e874259ce..2e3219caf3 100644
--- a/utils/convert.mjs
+++ b/utils/convert.mjs
@@ -7,23 +7,46 @@ import { descriptionString, typeObject } from './shared-helpers.mjs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
-const data = JSON.parse(fs.readFileSync(path.join(__dirname, '../docs/data.json')));
+const data = JSON.parse(
+ fs.readFileSync(path.join(__dirname, '../docs/data.json'))
+);
// Generate documentation used in the p5.js reference. This data will get read in
// the p5.js-website repo: https://github.com/processing/p5.js-website/
const htmlStrategy = {
- shouldSkipEntry: (entry) =>
+ shouldSkipEntry: entry =>
// Skip static methods on p5.Vector for now because the names clash with
// the non static versions
entry.scope === 'static' &&
entry.memberof === 'Vector' &&
- ['add', 'sub', 'mult', 'div', 'copy', 'rem', 'rotate', 'dot', 'cross', 'dist', 'lerp', 'slerp',
- 'mag', 'magSq', 'normalize', 'limit', 'setMag', 'heading', 'angleBetween', 'reflect',
- 'array', 'equals'].includes(entry.name),
-
- processDescription: (desc) => descriptionString(desc),
-
- processType: (type) => typeObject(type)
+ [
+ 'add',
+ 'sub',
+ 'mult',
+ 'div',
+ 'copy',
+ 'rem',
+ 'rotate',
+ 'dot',
+ 'cross',
+ 'dist',
+ 'lerp',
+ 'slerp',
+ 'mag',
+ 'magSq',
+ 'normalize',
+ 'limit',
+ 'setMag',
+ 'heading',
+ 'angleBetween',
+ 'reflect',
+ 'array',
+ 'equals'
+ ].includes(entry.name),
+
+ processDescription: desc => descriptionString(desc),
+
+ processType: type => typeObject(type)
};
const processed = processData(data, htmlStrategy);
@@ -38,7 +61,6 @@ const converted = {
consts: processed.consts
};
-
// Register constant usage for the original convert.mjs functionality
const constUsage = {};
function registerConstantUsage(name, memberof, node) {
@@ -64,7 +86,12 @@ function registerConstantUsage(name, memberof, node) {
// Register constant usage from processed data
for (const item of converted.classitems) {
- if (item.itemtype === 'property' && (item.name in converted.consts || item.kind === 'constant' || item.kind === 'typedef')) {
+ if (
+ item.itemtype === 'property' &&
+ (item.name in converted.consts ||
+ item.kind === 'constant' ||
+ item.kind === 'typedef')
+ ) {
constUsage[item.name] = constUsage[item.name] || new Set();
}
if (item.itemtype === 'method') {
@@ -84,7 +111,6 @@ for (const key in constUsage) {
converted.consts[key] = [...constUsage[key]];
}
-
// ============================================================================
// parameterData.json
// ============================================================================
@@ -173,7 +199,11 @@ function buildParamDocs(docs) {
for (let classitem of docs.classitems) {
// If `classitem` doesn't have overloads, then it's not a function—skip processing in this case
- if (classitem.name && classitem.class && classitem.hasOwnProperty('overloads')) {
+ if (
+ classitem.name &&
+ classitem.class &&
+ classitem.hasOwnProperty('overloads')
+ ) {
// Skip if the item already exists in newClassItems
if (
newClassItems[classitem.class] &&
@@ -215,7 +245,12 @@ function buildParamDocs(docs) {
}
fs.mkdirSync(path.join(__dirname, '../docs/reference'), { recursive: true });
-fs.writeFileSync(path.join(__dirname, '../docs/reference/data.json'), JSON.stringify(converted, null, 2));
-fs.writeFileSync(path.join(__dirname, '../docs/reference/data.min.json'), JSON.stringify(converted));
+fs.writeFileSync(
+ path.join(__dirname, '../docs/reference/data.json'),
+ JSON.stringify(converted, null, 2)
+);
+fs.writeFileSync(
+ path.join(__dirname, '../docs/reference/data.min.json'),
+ JSON.stringify(converted)
+);
buildParamDocs(JSON.parse(JSON.stringify(converted)));
-
diff --git a/utils/data-processor.mjs b/utils/data-processor.mjs
index 7349e10773..9fc72a20e8 100644
--- a/utils/data-processor.mjs
+++ b/utils/data-processor.mjs
@@ -8,7 +8,7 @@ import { getParams } from './shared-helpers.mjs';
export function processData(rawData, strategy) {
const allData = getAllEntries(rawData);
-
+
const processed = {
modules: {},
classes: {},
@@ -21,7 +21,7 @@ export function processData(rawData, strategy) {
const fileModuleInfo = {};
const modules = {};
const submodules = {};
-
+
for (const entry of allData) {
if (entry.tags?.some(tag => tag.title === 'module')) {
const module = entry.tags.find(tag => tag.title === 'module').name;
@@ -37,7 +37,8 @@ export function processData(rawData, strategy) {
for: undefined
};
fileModuleInfo[file].module = module;
- fileModuleInfo[file].submodule = fileModuleInfo[file].submodule || submodule;
+ fileModuleInfo[file].submodule =
+ fileModuleInfo[file].submodule || submodule;
fileModuleInfo[file].for = fileModuleInfo[file].for || forEntry;
modules[module] = modules[module] || {
@@ -70,8 +71,11 @@ export function processData(rawData, strategy) {
const entryForTagValue = entryForTag?.description;
const file = entry.context?.file;
let { module, submodule, for: forEntry } = fileModuleInfo[file] || {};
- module = entry.tags?.find(tag => tag.title === 'module')?.description || module;
- submodule = entry.tags?.find(tag => tag.title === 'submodule')?.description || submodule;
+ module =
+ entry.tags?.find(tag => tag.title === 'module')?.description || module;
+ submodule =
+ entry.tags?.find(tag => tag.title === 'submodule')?.description ||
+ submodule;
let memberof = entry.memberof;
if (memberof === 'fn') memberof = 'p5';
if (memberof && memberof !== 'p5' && !memberof.startsWith('p5.')) {
@@ -83,7 +87,9 @@ export function processData(rawData, strategy) {
function locationInfo(entry) {
return {
- file: entry.context?.file ? entry.context.file.slice(entry.context.file.indexOf('src/')) : '',
+ file: entry.context?.file
+ ? entry.context.file.slice(entry.context.file.indexOf('src/'))
+ : '',
line: entry.context?.loc?.start?.line || 1
};
}
@@ -103,37 +109,47 @@ export function processData(rawData, strategy) {
}
function getAlt(entry) {
- return entry
- .tags
- ?.filter(tag => tag.title === 'alt')
- ?.map(tag => tag.description)
- ?.join('\n') || undefined;
+ return (
+ entry.tags
+ ?.filter(tag => tag.title === 'alt')
+ ?.map(tag => tag.description)
+ ?.join('\n') || undefined
+ );
}
// Process constants, typedefs, and properties
const processedNames = new Set();
for (const entry of allData) {
- if (entry.kind === 'constant' || entry.kind === 'typedef' || entry.kind === 'property' ||
- (entry.properties && entry.properties.length > 0 && entry.properties[0].title === 'property') ||
- entry.tags?.some(tag => tag.title === 'property')) {
+ if (
+ entry.kind === 'constant' ||
+ entry.kind === 'typedef' ||
+ entry.kind === 'property' ||
+ (entry.properties &&
+ entry.properties.length > 0 &&
+ entry.properties[0].title === 'property') ||
+ entry.tags?.some(tag => tag.title === 'property')
+ ) {
const { module, submodule, forEntry } = getModuleInfo(entry);
-
+
// Apply strategy filter
- if (strategy.shouldSkipEntry && strategy.shouldSkipEntry(entry, { module, submodule, forEntry })) {
+ if (
+ strategy.shouldSkipEntry &&
+ strategy.shouldSkipEntry(entry, { module, submodule, forEntry })
+ ) {
continue;
}
- const name = entry.name ||
+ const name =
+ entry.name ||
(entry.properties || [])[0]?.name ||
entry.tags?.find(t => t.title === 'property')?.name;
-
+
// Skip duplicates based on name + class combination
const key = `${name}:${forEntry || 'p5'}`;
if (processedNames.has(key)) {
continue;
}
processedNames.add(key);
-
// For properties, get type from the property definition
const propertyType = entry.properties?.[0]?.type || entry.type;
@@ -153,7 +169,7 @@ export function processData(rawData, strategy) {
class: forEntry || 'p5',
beta: entry.tags?.some(t => t.title === 'beta') || undefined,
webgpu: entry.tags?.some(t => t.title === 'webgpu') || undefined,
- webgpuOnly: entry.tags?.some(t => t.title === 'webgpuOnly') || undefined,
+ webgpuOnly: entry.tags?.some(t => t.title === 'webgpuOnly') || undefined
};
processed.classitems.push(item);
@@ -165,9 +181,12 @@ export function processData(rawData, strategy) {
for (const entry of allData) {
if (entry.kind === 'class') {
const { module, submodule } = getModuleInfo(entry);
-
+
// Apply strategy filter
- if (strategy.shouldSkipEntry && strategy.shouldSkipEntry(entry, { module, submodule })) {
+ if (
+ strategy.shouldSkipEntry &&
+ strategy.shouldSkipEntry(entry, { module, submodule })
+ ) {
continue;
}
@@ -181,11 +200,14 @@ export function processData(rawData, strategy) {
alt: getAlt(entry),
params: getParams(entry).map(p => ({
name: p.name,
- description: p.description && strategy.processDescription(p.description),
+ description:
+ p.description && strategy.processDescription(p.description),
...strategy.processType(p.type, p)
})),
return: entry.returns?.[0] && {
- description: strategy.processDescription(entry.returns[0].description),
+ description: strategy.processDescription(
+ entry.returns[0].description
+ ),
...strategy.processType(entry.returns[0].type)
},
is_constructor: 1,
@@ -193,7 +215,7 @@ export function processData(rawData, strategy) {
submodule,
beta: entry.tags?.some(t => t.title === 'beta') || undefined,
webgpu: entry.tags?.some(t => t.title === 'webgpu') || undefined,
- webgpuOnly: entry.tags?.some(t => t.title === 'webgpuOnly') || undefined,
+ webgpuOnly: entry.tags?.some(t => t.title === 'webgpuOnly') || undefined
};
// The @private tag doesn't seem to end up in the Documentation.js output.
@@ -211,9 +233,12 @@ export function processData(rawData, strategy) {
for (const entry of allData) {
if (entry.kind === 'function' && entry.properties?.length === 0) {
const { module, submodule, forEntry } = getModuleInfo(entry);
-
+
// Apply strategy filter
- if (strategy.shouldSkipEntry && strategy.shouldSkipEntry(entry, { module, submodule, forEntry })) {
+ if (
+ strategy.shouldSkipEntry &&
+ strategy.shouldSkipEntry(entry, { module, submodule, forEntry })
+ ) {
continue;
}
@@ -227,7 +252,7 @@ export function processData(rawData, strategy) {
}
const className = memberof || forEntry || 'p5';
-
+
// Skip private methods
if (entry.name.startsWith('_')) continue;
@@ -244,10 +269,12 @@ export function processData(rawData, strategy) {
...locationInfo(entry),
...deprecationInfo(entry),
itemtype: 'method',
- description: prevItem?.description || strategy.processDescription(entry.description),
+ description:
+ prevItem?.description ||
+ strategy.processDescription(entry.description),
example: [
...(prevItem?.example || []),
- ...entry.examples?.map(getExample) || []
+ ...(entry.examples?.map(getExample) || [])
],
alt: getAlt(entry),
overloads: [
@@ -255,30 +282,49 @@ export function processData(rawData, strategy) {
{
params: getParams(entry).map(p => ({
name: p.name,
- description: p.description && strategy.processDescription(p.description),
+ description:
+ p.description && strategy.processDescription(p.description),
...strategy.processType(p.type, p)
})),
return: entry.returns?.[0] && {
- description: strategy.processDescription(entry.returns[0].description),
+ description: strategy.processDescription(
+ entry.returns[0].description
+ ),
...strategy.processType(entry.returns[0].type)
},
- chainable: entry.tags?.some(tag => tag.title === 'chainable') ? 1 : undefined
+ chainable: entry.tags?.some(tag => tag.title === 'chainable')
+ ? 1
+ : undefined
}
],
- return: prevItem?.return || entry.returns?.[0] && {
- description: strategy.processDescription(entry.returns[0].description),
- ...strategy.processType(entry.returns[0].type)
- },
+ return:
+ prevItem?.return ||
+ (entry.returns?.[0] && {
+ description: strategy.processDescription(
+ entry.returns[0].description
+ ),
+ ...strategy.processType(entry.returns[0].type)
+ }),
class: className,
static: entry.scope === 'static' && 1,
module: prevItem?.module ?? module,
submodule: prevItem?.submodule ?? submodule,
- beta: prevItem?.beta || entry.tags?.some(t => t.title === 'beta') || undefined,
- webgpu: prevItem?.webgpu || entry.tags?.some(t => t.title === 'webgpu') || undefined,
- webgpuOnly: prevItem?.webgpuOnly || entry.tags?.some(t => t.title === 'webgpuOnly') || undefined,
+ beta:
+ prevItem?.beta ||
+ entry.tags?.some(t => t.title === 'beta') ||
+ undefined,
+ webgpu:
+ prevItem?.webgpu ||
+ entry.tags?.some(t => t.title === 'webgpu') ||
+ undefined,
+ webgpuOnly:
+ prevItem?.webgpuOnly ||
+ entry.tags?.some(t => t.title === 'webgpuOnly') ||
+ undefined
};
- processed.classMethods[className] = processed.classMethods[className] || {};
+ processed.classMethods[className] =
+ processed.classMethods[className] || {};
processed.classMethods[className][methodKey] = item;
}
}
@@ -291,4 +337,4 @@ export function processData(rawData, strategy) {
}
return processed;
-}
\ No newline at end of file
+}
diff --git a/utils/patch.mjs b/utils/patch.mjs
index 841ac9c703..4579301926 100644
--- a/utils/patch.mjs
+++ b/utils/patch.mjs
@@ -1,26 +1,24 @@
import fs from 'fs';
export function applyPatches() {
- const cache = {};
+ const cache = {};
const patched = {};
-
+
const replace = (path, src, dest) => {
if (Array.isArray(path)) {
path.forEach(path => replace(path, src, dest));
return;
}
try {
- if (!path.startsWith("types/"))
- path = "types/" + path;
+ if (!path.startsWith('types/')) path = 'types/' + path;
- const before = patched[path] ??
- (cache[path] ??= fs.readFileSync("./" + path, { encoding: 'utf-8' }));
+ const before =
+ patched[path] ??
+ (cache[path] ??= fs.readFileSync('./' + path, { encoding: 'utf-8' }));
const after = before.replaceAll(src, dest);
- if (after !== before)
- patched[path] = after;
- else
- console.error(`A patch failed in ${path}:\n -${src}\n +${dest}`);
+ if (after !== before) patched[path] = after;
+ else console.error(`A patch failed in ${path}:\n -${src}\n +${dest}`);
} catch (err) {
console.error(err);
}
@@ -28,8 +26,8 @@ export function applyPatches() {
// TODO: Handle this better in the docs instead of patching
replace(
- "p5.d.ts",
- "constructor(detailX?: number, detailY?: number, callback?: Function);",
+ 'p5.d.ts',
+ 'constructor(detailX?: number, detailY?: number, callback?: Function);',
`constructor(
detailX?: number,
detailY?: number,
@@ -39,9 +37,9 @@ export function applyPatches() {
// https://github.com/p5-types/p5.ts/issues/31
// #todo: add readonly to appropriate array params, either here or in doc comments
replace(
- ["p5.d.ts", "global.d.ts"],
- "random(choices: any[]): any;",
- "random(choices: readonly T[]): T;"
+ ['p5.d.ts', 'global.d.ts'],
+ 'random(choices: any[]): any;',
+ 'random(choices: readonly T[]): T;'
);
replace(
@@ -53,7 +51,7 @@ export function applyPatches() {
replace(
'p5.d.ts',
'textToContours(str: string, x: number, y: number, options?: { sampleFactor?: number; simplifyThreshold?: number }): object[][];',
- 'textToContours(str: string, x: number, y: number, options?: { sampleFactor?: number; simplifyThreshold?: number }): { x: number; y: number; alpha: number }[][];',
+ 'textToContours(str: string, x: number, y: number, options?: { sampleFactor?: number; simplifyThreshold?: number }): { x: number; y: number; alpha: number }[][];'
);
replace(
@@ -77,7 +75,7 @@ export function applyPatches() {
'class __Graphics extends p5.Element {',
`class __Graphics extends p5.Element {
elt: HTMLCanvasElement;
- `,
+ `
);
// Type .elt more specifically for audio and video elements
@@ -86,28 +84,28 @@ export function applyPatches() {
`class MediaElement extends Element {
elt: HTMLAudioElement | HTMLVideoElement;`,
`class MediaElement extends Element {
- elt: T;`,
+ elt: T;`
);
replace(
['p5.d.ts', 'global.d.ts'],
/createAudio\(src\?: string \| string\[\], callback\?: Function\): ([pP]5)\.MediaElement;/g,
- 'createAudio(src?: string | string[], callback?: (video: $1.MediaElement) => any): $1.MediaElement;',
+ 'createAudio(src?: string | string[], callback?: (video: $1.MediaElement) => any): $1.MediaElement;'
);
replace(
['p5.d.ts', 'global.d.ts'],
/createVideo\(src\?: string \| string\[\], callback\?: Function\): ([pP]5)\.MediaElement;/g,
- 'createVideo(src?: string | string[], callback?: (video: $1.MediaElement) => any): $1.MediaElement;',
+ 'createVideo(src?: string | string[], callback?: (video: $1.MediaElement) => any): $1.MediaElement;'
);
// More callback types
replace(
['p5.d.ts', 'global.d.ts'],
/createFileInput\(callback: Function, multiple\?: boolean\): ([pP]5)\.Element;/g,
- 'createFileInput(callback: (input: $1.File) => any, multiple?: boolean): $1.Element;',
+ 'createFileInput(callback: (input: $1.File) => any, multiple?: boolean): $1.Element;'
);
replace(
['p5.d.ts', 'global.d.ts'],
- /loadFont\((.+), successCallback\?: Function, (.+)\): Promise\<([pP]5)\.Font\>;/g,
+ /loadFont\((.+), successCallback\?: Function, (.+)\): Promise<([pP]5)\.Font>;/g,
'loadFont($1, successCallback?: (font: $3.Font) => any, $2): Promise<$3.Font>;'
);
@@ -120,7 +118,7 @@ export function applyPatches() {
replace(
'p5.d.ts',
'fontBounds(str: string, x: number, y: number, width?: number, height?: number): object;',
- 'fontBounds(str: string, x: number, y: number, width?: number, height?: number): { x: number; y: number; w: number; h: number };',
+ 'fontBounds(str: string, x: number, y: number, width?: number, height?: number): { x: number; y: number; w: number; h: number };'
);
replace(
'p5.d.ts',
@@ -130,7 +128,7 @@ export function applyPatches() {
replace(
'p5.d.ts',
'textBounds(str: string, x: number, y: number, width?: number, height?: number): object;',
- 'textBounds(str: string, x: number, y: number, width?: number, height?: number): { x: number; y: number; w: number; h: number };',
+ 'textBounds(str: string, x: number, y: number, width?: number, height?: number): { x: number; y: number; w: number; h: number };'
);
// Document Typr
@@ -169,7 +167,7 @@ export function applyPatches() {
for (const [path, data] of Object.entries(patched)) {
try {
console.log(`Patched ${path}`);
- fs.writeFileSync("./" + path, data);
+ fs.writeFileSync('./' + path, data);
} catch (err) {
console.error(err);
}
diff --git a/utils/sample-linter.mjs b/utils/sample-linter.mjs
index 08b4fb94e1..64d64548f2 100644
--- a/utils/sample-linter.mjs
+++ b/utils/sample-linter.mjs
@@ -1,7 +1,7 @@
'use strict';
const EOL = '\n';
import { ESLint } from 'eslint';
-import dataDoc from '../docs/reference/data.min.json' assert {type: 'json'};
+import dataDoc from '../docs/reference/data.min.json' assert { type: 'json' };
// envs: ['eslint-samples/p5'],
const itemtypes = ['method', 'property'];
@@ -9,9 +9,7 @@ const classes = ['p5'];
const globals = {};
dataDoc.classitems
- .filter(
- ci => classes.includes(ci.class) && itemtypes.includes(ci.itemtype)
- )
+ .filter(ci => classes.includes(ci.class) && itemtypes.includes(ci.itemtype))
.forEach(ci => {
globals[ci.name] = true;
});
@@ -63,7 +61,7 @@ const plugin = {
processors: {
'.js': {
supportsAutofix: true,
- preprocess: function(text) {
+ preprocess: function (text) {
globalLines = splitLines(text);
let m;
@@ -101,7 +99,7 @@ const plugin = {
return globalSamples.map(s => s.code + userFunctionTrailer);
},
- postprocess: function(sampleMessages, filename) {
+ postprocess: function (sampleMessages, filename) {
const problems = [];
for (let i = 0; i < sampleMessages.length; i++) {
@@ -177,7 +175,7 @@ async function eslintFiles(opts, filesSrc) {
'eslint-samples': plugin
},
overrideConfig: {
- 'plugins': ['eslint-samples']
+ plugins: ['eslint-samples']
},
fix: opts.fix
});
@@ -194,18 +192,21 @@ async function eslintFiles(opts, filesSrc) {
}
let results = await eslint.lintFiles(filesSrc);
- const report = results.reduce((acc, result) => {
- acc.errorCount += result.errorCount;
- acc.warningCount += result.warningCount;
- acc.fixableErrorCount += result.fixableErrorCount;
- acc.fixableWarningCount += result.fixableWarningCount;
- return acc;
- }, {
- errorCount: 0,
- warningCount: 0,
- fixableErrorCount: 0,
- fixableWarningCount: 0
- });
+ const report = results.reduce(
+ (acc, result) => {
+ acc.errorCount += result.errorCount;
+ acc.warningCount += result.warningCount;
+ acc.fixableErrorCount += result.fixableErrorCount;
+ acc.fixableWarningCount += result.fixableWarningCount;
+ return acc;
+ },
+ {
+ errorCount: 0,
+ warningCount: 0,
+ fixableErrorCount: 0,
+ fixableWarningCount: 0
+ }
+ );
if (opts.quiet) {
results = ESLint.getErrorResults(results);
@@ -220,7 +221,7 @@ async function eslintFiles(opts, filesSrc) {
function splitLines(text) {
const lines = [];
- lines.lineFromIndex = function(index) {
+ lines.lineFromIndex = function (index) {
const lines = this;
const lineCount = lines.length;
for (let i = 0; i < lineCount; i++) {
@@ -246,9 +247,8 @@ function splitLines(text) {
return lines;
}
-eslintFiles(null, process.argv.slice(2))
- .then(result => {
- if(result === true) process.exit(0);
- console.log(result.output);
- process.exit(result.report.errorCount === 0 ? 0 : 1);
- });
+eslintFiles(null, process.argv.slice(2)).then(result => {
+ if (result === true) process.exit(0);
+ console.log(result.output);
+ process.exit(result.report.errorCount === 0 ? 0 : 1);
+});
diff --git a/utils/shared-helpers.mjs b/utils/shared-helpers.mjs
index e8fb5791bb..b13d2b26f6 100644
--- a/utils/shared-helpers.mjs
+++ b/utils/shared-helpers.mjs
@@ -12,7 +12,7 @@ function getEntries(entry) {
}
export function getAllEntries(arr = []) {
- return arr.flatMap(entry => entry ? getEntries(entry) : []);
+ return arr.flatMap(entry => (entry ? getEntries(entry) : []));
}
export function descriptionString(node, parent) {
@@ -34,16 +34,24 @@ export function descriptionString(node, parent) {
classes.push(node.meta);
}
if (classes.length > 0) {
- attrs=` class="${classes.join(' ')}"`;
+ attrs = ` class="${classes.join(' ')}"`;
}
return `${node.value.replace(//g, '>')}
`;
} else if (node.type === 'inlineCode') {
return '' + node.value + '';
} else if (node.type === 'list') {
const tag = node.type === 'ordered' ? 'ol' : 'ul';
- return `<${tag}>` + node.children.map(n => descriptionString(n, node)).join('') + `${tag}>`;
+ return (
+ `<${tag}>` +
+ node.children.map(n => descriptionString(n, node)).join('') +
+ `${tag}>`
+ );
} else if (node.type === 'listItem') {
- return '- ' + node.children.map(n => descriptionString(n, node)).join('') + '
';
+ return (
+ '- ' +
+ node.children.map(n => descriptionString(n, node)).join('') +
+ '
'
+ );
} else if (node.value) {
return node.value;
} else if (node.children) {
@@ -60,20 +68,31 @@ export function descriptionStringForTypeScript(node, parent) {
} else if (node.type === 'text') {
return node.value;
} else if (node.type === 'paragraph') {
- const content = node.children.map(n => descriptionStringForTypeScript(n, node)).join('');
+ const content = node.children
+ .map(n => descriptionStringForTypeScript(n, node))
+ .join('');
return content + '\n\n'; // Skip HTML tags for TypeScript
} else if (node.type === 'code') {
return `\`${node.value}\``;
} else if (node.type === 'inlineCode') {
return `\`${node.value}\``;
} else if (node.type === 'list') {
- return node.children.map(n => descriptionStringForTypeScript(n, node)).join('') + '\n';
+ return (
+ node.children.map(n => descriptionStringForTypeScript(n, node)).join('') +
+ '\n'
+ );
} else if (node.type === 'listItem') {
- return '- ' + node.children.map(n => descriptionStringForTypeScript(n, node)).join('') + '\n';
+ return (
+ '- ' +
+ node.children.map(n => descriptionStringForTypeScript(n, node)).join('') +
+ '\n'
+ );
} else if (node.value) {
return node.value;
} else if (node.children) {
- return node.children.map(n => descriptionStringForTypeScript(n, node)).join('');
+ return node.children
+ .map(n => descriptionStringForTypeScript(n, node))
+ .join('');
} else {
return '';
}
@@ -91,10 +110,7 @@ export function typeObject(node) {
};
} else if (node.type === 'TypeApplication') {
const { type: typeName } = typeObject(node.expression);
- if (
- typeName === 'Array' &&
- node.applications.length === 1
- ) {
+ if (typeName === 'Array' && node.applications.length === 1) {
return {
type: `${typeObject(node.applications[0]).type}[]`
};
@@ -112,7 +128,9 @@ export function typeObject(node) {
}
return { type: signature };
} else if (node.type === 'ArrayType') {
- return { type: `[${node.elements.map(e => typeObject(e).type).join(', ')}]` };
+ return {
+ type: `[${node.elements.map(e => typeObject(e).type).join(', ')}]`
+ };
} else if (node.type === 'RestType') {
return { type: typeObject(node.expression).type, rest: true };
} else {
@@ -132,22 +150,25 @@ export function getParams(entry) {
// instead convert it to a string. We want a slightly different conversion to
// string, so we match these params to the Documentation.js-provided `params`
// array and grab the description from those.
- return (entry.tags || [])
+ return (
+ (entry.tags || [])
- // Filter out the nested parameters (eg. options.extrude),
- // to be treated as part of parent parameters (eg. options)
- // and not separate entries
- .filter(t => t.title === 'param' && !t.name.includes('.'))
- .map(node => {
- const param = (entry.params || [])
- .find(param => param.name === node.name);
- return {
- ...node,
- description: param?.description || {
- type: 'html',
- value: node.description
- },
- properties: param?.properties // Preserve properties array for nested object parameters
- };
- });
-}
\ No newline at end of file
+ // Filter out the nested parameters (eg. options.extrude),
+ // to be treated as part of parent parameters (eg. options)
+ // and not separate entries
+ .filter(t => t.title === 'param' && !t.name.includes('.'))
+ .map(node => {
+ const param = (entry.params || []).find(
+ param => param.name === node.name
+ );
+ return {
+ ...node,
+ description: param?.description || {
+ type: 'html',
+ value: node.description
+ },
+ properties: param?.properties // Preserve properties array for nested object parameters
+ };
+ })
+ );
+}
diff --git a/utils/typescript.mjs b/utils/typescript.mjs
index 382c9e90fa..6863e5e59c 100644
--- a/utils/typescript.mjs
+++ b/utils/typescript.mjs
@@ -17,7 +17,9 @@ if (fs.existsSync(typesDir)) {
}
fs.mkdirSync(typesDir, { recursive: true });
-const rawData = JSON.parse(fs.readFileSync(path.join(__dirname, '../docs/data.json')));
+const rawData = JSON.parse(
+ fs.readFileSync(path.join(__dirname, '../docs/data.json'))
+);
// Pre-build constants lookup
import { getAllEntries } from './shared-helpers.mjs';
@@ -40,47 +42,55 @@ function processStrandsFunctions() {
const strandsMethods = [
{
name: 'instanceID',
- overloads: [{
- params: [],
- return: {
- type: { type: 'NameExpression', name: 'any' } // Return 'any' for strands nodes
+ overloads: [
+ {
+ params: [],
+ return: {
+ type: { type: 'NameExpression', name: 'any' } // Return 'any' for strands nodes
+ }
}
- }],
+ ],
description: `Returns the ID when drawing many instances`,
static: false
},
{
name: 'discard',
- overloads: [{
- params: [],
- }],
+ overloads: [
+ {
+ params: []
+ }
+ ],
description: `Discards the current pixel`,
static: false
},
{
name: 'getTexture',
- overloads: [{
- params: [
- {
- name: 'tex',
- type: { type: 'NameExpression', name: 'any' },
- optional: false,
- },
- {
- name: 'coord',
- type: { type: 'NameExpression', name: 'any' },
- optional: false,
- },
- ],
- return: {
- type: { type: 'NameExpression', name: 'any' } // Return 'any' for strands nodes
+ overloads: [
+ {
+ params: [
+ {
+ name: 'tex',
+ type: { type: 'NameExpression', name: 'any' },
+ optional: false
+ },
+ {
+ name: 'coord',
+ type: { type: 'NameExpression', name: 'any' },
+ optional: false
+ }
+ ],
+ return: {
+ type: { type: 'NameExpression', name: 'any' } // Return 'any' for strands nodes
+ }
}
- }],
+ ]
}
];
// Add ALL GLSL builtin functions (both isp5Function: true and false)
- for (const [functionName, overloads] of Object.entries(builtInGLSLFunctions)) {
+ for (const [functionName, overloads] of Object.entries(
+ builtInGLSLFunctions
+ )) {
// Create method definition with simplified any types for all overloads
const method = {
name: functionName,
@@ -113,52 +123,56 @@ function processStrandsFunctions() {
const typeAliases = [];
if (/^[ib]vec/.test(typeInfo.fnName)) {
- pascalTypeName = typeInfo.fnName
- .slice(0, 2).toUpperCase()
- + typeInfo.fnName
- .slice(2)
- .toLowerCase();
+ pascalTypeName =
+ typeInfo.fnName.slice(0, 2).toUpperCase() +
+ typeInfo.fnName.slice(2).toLowerCase();
typeAliases.push(pascalTypeName.replace('Vec', 'Vector'));
} else {
- pascalTypeName = typeInfo.fnName.charAt(0).toUpperCase()
- + typeInfo.fnName.slice(1);
+ pascalTypeName =
+ typeInfo.fnName.charAt(0).toUpperCase() + typeInfo.fnName.slice(1);
if (pascalTypeName === 'Sampler2D') {
- typeAliases.push('Texture')
+ typeAliases.push('Texture');
} else if (/^vec/.test(typeInfo.fnName)) {
typeAliases.push(pascalTypeName.replace('Vec', 'Vector'));
}
}
- typeMethods.push(...[pascalTypeName, ...typeAliases].flatMap((typeName) => [
- {
- name: `uniform${typeName}`,
- overloads: [{
- params: [
+ typeMethods.push(
+ ...[pascalTypeName, ...typeAliases].flatMap(typeName => [
+ {
+ name: `uniform${typeName}`,
+ overloads: [
{
- name: 'defaultValue',
- type: { type: 'NameExpression', name: 'any' },
- optional: true
+ params: [
+ {
+ name: 'defaultValue',
+ type: { type: 'NameExpression', name: 'any' },
+ optional: true
+ }
+ ],
+ return: {
+ type: { type: 'NameExpression', name: 'any' }
+ }
}
],
- return: {
- type: { type: 'NameExpression', name: 'any' }
- }
- }],
- description: `Create a ${pascalTypeName} uniform variable`,
- static: false
- },
- ...['varying', 'shared'].map((prefix) => ({
- name: `${prefix}${typeName}`,
- overloads: [{
- params: [],
- return: {
- type: { type: 'NameExpression', name: 'any' }
- }
- }],
- description: `Create a shared ${pascalTypeName} to pass data between hooks`,
- static: false
- }))
- ]));
+ description: `Create a ${pascalTypeName} uniform variable`,
+ static: false
+ },
+ ...['varying', 'shared'].map(prefix => ({
+ name: `${prefix}${typeName}`,
+ overloads: [
+ {
+ params: [],
+ return: {
+ type: { type: 'NameExpression', name: 'any' }
+ }
+ }
+ ],
+ description: `Create a shared ${pascalTypeName} to pass data between hooks`,
+ static: false
+ }))
+ ])
+ );
}
// Add type casting functions (DataType constructor functions)
@@ -171,18 +185,20 @@ function processStrandsFunctions() {
const typeInfo = DataType[type];
const castingMethod = {
name: typeInfo.fnName,
- overloads: [{
- params: [
- {
- name: 'value',
- type: { type: 'NameExpression', name: 'any' },
- optional: false
+ overloads: [
+ {
+ params: [
+ {
+ name: 'value',
+ type: { type: 'NameExpression', name: 'any' },
+ optional: false
+ }
+ ],
+ return: {
+ type: { type: 'NameExpression', name: 'any' }
}
- ],
- return: {
- type: { type: 'NameExpression', name: 'any' }
}
- }],
+ ],
description: `GLSL type constructor for ${typeInfo.fnName}`,
static: false
};
@@ -199,10 +215,17 @@ function convertTypeToTypeScript(typeNode, options = {}) {
// Validate that typeNode is always an object
if (typeof typeNode !== 'object' || Array.isArray(typeNode)) {
- throw new Error(`convertTypeToTypeScript expects an object, got: ${typeof typeNode} - ${JSON.stringify(typeNode)}`);
+ throw new Error(
+ `convertTypeToTypeScript expects an object, got: ${typeof typeNode} - ${JSON.stringify(typeNode)}`
+ );
}
- const { currentClass = null, isInsideNamespace = false, inGlobalMode = false, isConstantDef = false } = options;
+ const {
+ currentClass = null,
+ isInsideNamespace = false,
+ inGlobalMode = false,
+ isConstantDef = false
+ } = options;
switch (typeNode.type) {
case 'NameExpression': {
@@ -210,19 +233,19 @@ function convertTypeToTypeScript(typeNode, options = {}) {
// Handle primitive types
const primitiveTypes = {
- 'String': 'string',
- 'Number': 'number',
- 'Integer': 'number',
- 'Boolean': 'boolean',
- 'Void': 'void',
- 'Object': 'object',
- 'Any': 'any',
- 'Array': 'any[]',
- 'Promise': 'Promise',
- 'Function': 'Function',
- 'HTMLElement': 'HTMLElement',
- 'Event': 'Event',
- 'Request': 'Request'
+ String: 'string',
+ Number: 'number',
+ Integer: 'number',
+ Boolean: 'boolean',
+ Void: 'void',
+ Object: 'object',
+ Any: 'any',
+ Array: 'any[]',
+ Promise: 'Promise',
+ Function: 'Function',
+ HTMLElement: 'HTMLElement',
+ Event: 'Event',
+ Request: 'Request'
};
if (primitiveTypes[typeName]) {
@@ -230,7 +253,10 @@ function convertTypeToTypeScript(typeNode, options = {}) {
}
// Handle self-referential types within the same class
- if (currentClass && (typeName === `p5.${currentClass}` || typeName === currentClass)) {
+ if (
+ currentClass &&
+ (typeName === `p5.${currentClass}` || typeName === currentClass)
+ ) {
return currentClass;
}
@@ -279,7 +305,10 @@ function convertTypeToTypeScript(typeNode, options = {}) {
const baseTypeName = typeNode.expression.name;
if (baseTypeName === 'Array' && typeNode.applications.length === 1) {
- const innerType = convertTypeToTypeScript(typeNode.applications[0], options);
+ const innerType = convertTypeToTypeScript(
+ typeNode.applications[0],
+ options
+ );
return `${innerType}[]`;
}
@@ -319,7 +348,9 @@ function convertTypeToTypeScript(typeNode, options = {}) {
return 'undefined';
case 'ArrayType': {
- const innerTypes = typeNode.elements.map(e => convertTypeToTypeScript(e, options));
+ const innerTypes = typeNode.elements.map(e =>
+ convertTypeToTypeScript(e, options)
+ );
return `[${innerTypes.join(', ')}]`;
}
@@ -347,7 +378,10 @@ function convertTypeToTypeScript(typeNode, options = {}) {
// Check if typedef represents a real object shape
function hasTypedefProperties(typedefEntry) {
- if (!Array.isArray(typedefEntry.properties) || typedefEntry.properties.length === 0) {
+ if (
+ !Array.isArray(typedefEntry.properties) ||
+ typedefEntry.properties.length === 0
+ ) {
return false;
}
// Reject self-referential single-property typedefs
@@ -377,7 +411,11 @@ function convertFunctionTypeForInterface(typeNode, options) {
if (currentParam && currentParam.type === 'ParameterType') {
typeObj = currentParam.expression;
paramName = currentParam.name ?? `p${i}`;
- } else if (currentParam && typeof currentParam.type === 'object' && currentParam.type !== null) {
+ } else if (
+ currentParam &&
+ typeof currentParam.type === 'object' &&
+ currentParam.type !== null
+ ) {
typeObj = currentParam.type;
paramName = currentParam.name ?? `p${i}`;
} else {
@@ -401,15 +439,21 @@ function convertFunctionTypeForInterface(typeNode, options) {
}
// Generate a TypeScript interface from a typedef with @property fields
-function generateTypedefInterface(name, typedefEntry, options = {}, indent = 2) {
+function generateTypedefInterface(
+ name,
+ typedefEntry,
+ options = {},
+ indent = 2
+) {
const pad = ' '.repeat(indent);
const innerPad = ' '.repeat(indent + 2);
let output = '';
if (typedefEntry.description) {
- const descStr = typeof typedefEntry.description === 'string'
- ? typedefEntry.description
- : descriptionStringForTypeScript(typedefEntry.description);
+ const descStr =
+ typeof typedefEntry.description === 'string'
+ ? typedefEntry.description
+ : descriptionStringForTypeScript(typedefEntry.description);
if (descStr) {
output += `${pad}/**\n`;
output += formatJSDocComment(descStr, indent) + '\n';
@@ -427,9 +471,10 @@ function generateTypedefInterface(name, typedefEntry, options = {}, indent = 2)
const optMark = isOptional ? '?' : '';
if (prop.description) {
- const propDescStr = typeof prop.description === 'string'
- ? prop.description.trim()
- : descriptionStringForTypeScript(prop.description);
+ const propDescStr =
+ typeof prop.description === 'string'
+ ? prop.description.trim()
+ : descriptionStringForTypeScript(prop.description);
if (propDescStr) {
output += `${innerPad}/** ${propDescStr} */\n`;
}
@@ -443,7 +488,9 @@ function generateTypedefInterface(name, typedefEntry, options = {}, indent = 2)
const retPart = sig.substring(arrowIdx + 2).trim();
output += `${innerPad}${propName}${paramsPart}: ${retPart};\n`;
} else {
- const tsType = rawType ? convertTypeToTypeScript(rawType, options) : 'any';
+ const tsType = rawType
+ ? convertTypeToTypeScript(rawType, options)
+ : 'any';
output += `${innerPad}${propName}${optMark}: ${tsType};\n`;
}
}
@@ -459,7 +506,7 @@ const typescriptStrategy = {
return context.module === 'Foundation';
},
- processDescription: (desc) => descriptionStringForTypeScript(desc),
+ processDescription: desc => descriptionStringForTypeScript(desc),
processType: (type, param) => {
// Return an object with the original type preserved
@@ -491,10 +538,7 @@ const typescriptStrategy = {
const processed = processData(rawData, typescriptStrategy);
function normalizeIdentifier(name) {
- return (
- '0123456789'.includes(name[0]) ||
- name === 'class'
- ) ? '$' + name : name;
+ return '0123456789'.includes(name[0]) || name === 'class' ? '$' + name : name;
}
function formatJSDocComment(text, indentLevel = 0) {
@@ -506,23 +550,23 @@ function formatJSDocComment(text, indentLevel = 0) {
.map(line => line.trim())
.reduce((acc, line) => {
if (acc.length === 0 && line === '') return acc;
- if (acc.length > 0 && line === '' && acc[acc.length - 1] === '') return acc;
+ if (acc.length > 0 && line === '' && acc[acc.length - 1] === '')
+ return acc;
acc.push(line);
return acc;
}, [])
.filter((line, i, arr) => i < arr.length - 1 || line !== '');
- return lines
- .map(line => `${indent} * ${line}`)
- .join('\n');
+ return lines.map(line => `${indent} * ${line}`).join('\n');
}
function generateObjectInterface(param, allParams, options = {}) {
// Check if this is an object parameter (either required or optional)
- const isObjectParam = param.type && (
- (param.type.type === 'OptionalType' && param.type.expression?.name === 'Object') ||
- (param.type.type === 'NameExpression' && param.type.name === 'Object')
- );
+ const isObjectParam =
+ param.type &&
+ ((param.type.type === 'OptionalType' &&
+ param.type.expression?.name === 'Object') ||
+ (param.type.type === 'NameExpression' && param.type.name === 'Object'));
if (!isObjectParam || !param.name) {
return null;
@@ -530,18 +574,18 @@ function generateObjectInterface(param, allParams, options = {}) {
let nestedParams = [];
-
// First, check if the parameter has a properties array (JSDoc properties field)
if (param.properties && Array.isArray(param.properties)) {
- nestedParams = param.properties.filter(prop =>
- prop.name && prop.name.startsWith(param.name + '.')
+ nestedParams = param.properties.filter(
+ prop => prop.name && prop.name.startsWith(param.name + '.')
);
}
// Fallback: Look for nested parameters with dot notation in allParams
if (nestedParams.length === 0) {
- nestedParams = allParams.filter(p =>
- p.name && p.name.startsWith(param.name + '.') && p.name !== param.name
+ nestedParams = allParams.filter(
+ p =>
+ p.name && p.name.startsWith(param.name + '.') && p.name !== param.name
);
}
@@ -552,9 +596,14 @@ function generateObjectInterface(param, allParams, options = {}) {
// Generate interface properties
const properties = nestedParams.map(nestedParam => {
const propName = nestedParam.name.substring(param.name.length + 1); // Remove 'paramName.' prefix
- const propType = nestedParam.type ? convertTypeToTypeScript(nestedParam.type, options) : 'any';
+ const propType = nestedParam.type
+ ? convertTypeToTypeScript(nestedParam.type, options)
+ : 'any';
// Properties are optional if they have a default value or are explicitly marked as optional
- const isOptional = nestedParam.optional || nestedParam.type?.type === 'OptionalType' || nestedParam.default !== undefined;
+ const isOptional =
+ nestedParam.optional ||
+ nestedParam.type?.type === 'OptionalType' ||
+ nestedParam.default !== undefined;
return `${propName}${isOptional ? '?' : ''}: ${propType}`;
});
@@ -602,21 +651,31 @@ function generateMethodDeclaration(method, options = {}) {
if (method.overloads?.[0]?.params) {
method.overloads[0].params.forEach(param => {
if (param.description) {
- output += formatJSDocComment(`@param ${param.name} ${param.description}`, commentIndent) + '\n';
+ output +=
+ formatJSDocComment(
+ `@param ${param.name} ${param.description}`,
+ commentIndent
+ ) + '\n';
}
});
}
// Add return docs
if (method.return?.description) {
- output += formatJSDocComment(`@returns ${method.return.description}`, commentIndent) + '\n';
+ output +=
+ formatJSDocComment(
+ `@returns ${method.return.description}`,
+ commentIndent
+ ) + '\n';
}
output += `${indent} */\n`;
}
const staticPrefix = method.static ? 'static ' : '';
- const declarationPrefix = globalFunction ? 'function ' : `${indent}${staticPrefix}`;
+ const declarationPrefix = globalFunction
+ ? 'function '
+ : `${indent}${staticPrefix}`;
// Generate overload declarations
if (method.overloads && method.overloads.length > 0) {
@@ -626,7 +685,11 @@ function generateMethodDeclaration(method, options = {}) {
.join(', ');
let returnType = 'void';
- if (overload.chainable && !globalFunction && options.currentClass !== 'p5') {
+ if (
+ overload.chainable &&
+ !globalFunction &&
+ options.currentClass !== 'p5'
+ ) {
returnType = options.currentClass || 'this';
// TODO: Decide what should be chainable. Many of these are accidental / not thought through
} else if (overload.return && overload.return.type) {
@@ -645,7 +708,9 @@ function generateMethodDeclaration(method, options = {}) {
function generateClassDeclaration(classData) {
let output = '';
- const className = classData.name.startsWith('p5.') ? classData.name.substring(3) : classData.name;
+ const className = classData.name.startsWith('p5.')
+ ? classData.name.substring(3)
+ : classData.name;
const actualClassName = className === 'Graphics' ? '__Graphics' : className;
if (classData.description) {
@@ -654,14 +719,22 @@ function generateClassDeclaration(classData) {
output += ' */\n';
}
- const extendsClause = classData.extends ? ` extends ${classData.extends}` : '';
+ const extendsClause = classData.extends
+ ? ` extends ${classData.extends}`
+ : '';
output += ` class ${actualClassName}${extendsClause} {\n`;
// Constructor
if (classData.params?.length > 0) {
output += ' constructor(';
output += classData.params
- .map(param => generateParamDeclaration(param, { currentClass: className, isInsideNamespace: true }, classData.params))
+ .map(param =>
+ generateParamDeclaration(
+ param,
+ { currentClass: className, isInsideNamespace: true },
+ classData.params
+ )
+ )
.join(', ');
output += ');\n\n';
}
@@ -670,12 +743,14 @@ function generateClassDeclaration(classData) {
const originalClassName = classData.name;
// Class methods
- const classMethodsList = Object.values(processed.classMethods[originalClassName] || {});
+ const classMethodsList = Object.values(
+ processed.classMethods[originalClassName] || {}
+ );
const methodNames = new Set(classMethodsList.map(method => method.name));
// Class properties
- const classProperties = processed.classitems.filter(item =>
- item.class === originalClassName && item.itemtype === 'property'
+ const classProperties = processed.classitems.filter(
+ item => item.class === originalClassName && item.itemtype === 'property'
);
classProperties.forEach(prop => {
@@ -720,7 +795,11 @@ function generateTypeDefinitions() {
// First, define all constants at the top level with their actual values
const seenConstants = new Set();
const p5Constants = processed.classitems.filter(item => {
- if (item.class === 'p5' && item.itemtype === 'property' && item.name in processed.consts) {
+ if (
+ item.class === 'p5' &&
+ item.itemtype === 'property' &&
+ item.name in processed.consts
+ ) {
// Skip defineProperty, undefined and avoid duplicates
if (item.name === 'defineProperty' || !item.name) {
return false;
@@ -744,7 +823,10 @@ function generateTypeDefinitions() {
output += formatJSDocComment(constant.description, 0) + '\n';
output += ' */\n';
}
- const type = convertTypeToTypeScript(constant.type, { isInsideNamespace: false, isConstantDef: true });
+ const type = convertTypeToTypeScript(constant.type, {
+ isInsideNamespace: false,
+ isConstantDef: true
+ });
const isMutable = mutableProperties.has(constant.name);
const declaration = isMutable ? 'declare let' : 'declare const';
output += `${declaration} ${constant.name}: ${type};\n\n`;
@@ -754,18 +836,23 @@ function generateTypeDefinitions() {
// Generate main p5 class
output += 'declare class p5 {\n';
- output += ' constructor(sketch?: (p: p5) => void, node?: HTMLElement, sync?: boolean);\n\n';
+ output +=
+ ' constructor(sketch?: (p: p5) => void, node?: HTMLElement, sync?: boolean);\n\n';
const p5Options = { currentClass: 'p5', isInsideNamespace: false };
// Generate p5 static methods
- const p5StaticMethods = Object.values(processed.classMethods.p5 || {}).filter(method => method.static);
+ const p5StaticMethods = Object.values(processed.classMethods.p5 || {}).filter(
+ method => method.static
+ );
p5StaticMethods.forEach(method => {
output += generateMethodDeclaration(method, p5Options);
});
// Generate p5 instance methods
- const p5InstanceMethods = Object.values(processed.classMethods.p5 || {}).filter(method => !method.static);
+ const p5InstanceMethods = Object.values(
+ processed.classMethods.p5 || {}
+ ).filter(method => !method.static);
p5InstanceMethods.forEach(method => {
output += generateMethodDeclaration(method, p5Options);
});
@@ -803,7 +890,12 @@ function generateTypeDefinitions() {
const namespaceOptions = { isInsideNamespace: true };
for (const [name, typedefEntry] of Object.entries(typedefs)) {
if (hasTypedefProperties(typedefEntry)) {
- output += generateTypedefInterface(name, typedefEntry, namespaceOptions, 2);
+ output += generateTypedefInterface(
+ name,
+ typedefEntry,
+ namespaceOptions,
+ 2
+ );
}
}
@@ -816,7 +908,14 @@ function generateTypeDefinitions() {
// Generate placeholder types for private classes that we need to be able to
// reference, but have no public APIs
- const privateClasses = ['Renderer', 'Renderer2D', 'RendererGL', 'FramebufferTexture', 'Texture', 'Quat'];
+ const privateClasses = [
+ 'Renderer',
+ 'Renderer2D',
+ 'RendererGL',
+ 'FramebufferTexture',
+ 'Texture',
+ 'Quat'
+ ];
// Define base classes for private classes, if they should extend something
const privateClassBases = { Renderer: 'Element' };
for (const className of privateClasses) {
@@ -855,17 +954,26 @@ p5: P5;
globalDefinitions += `${constant.name}: typeof P5.${constant.name};\n\n`;
});
- const globalP5Methods = Object.values(processed.classMethods.p5 || {})
- .filter(method => !method.static && method.name !== 'p5');
+ const globalP5Methods = Object.values(processed.classMethods.p5 || {}).filter(
+ method => !method.static && method.name !== 'p5'
+ );
globalP5Methods.forEach(method => {
- globalDefinitions += generateMethodDeclaration(method, { currentClass: 'p5', isInsideNamespace: true, inGlobalMode: true });
+ globalDefinitions += generateMethodDeclaration(method, {
+ currentClass: 'p5',
+ isInsideNamespace: true,
+ inGlobalMode: true
+ });
});
// Add strands functions to global scope
const conflictingDOMFunctions = ['length']; // Add other conflicting function names here as needed
strandsMethods.forEach(method => {
if (!conflictingDOMFunctions.includes(method.name)) {
- globalDefinitions += generateMethodDeclaration(method, { currentClass: 'p5', isInsideNamespace: true, inGlobalMode: true });
+ globalDefinitions += generateMethodDeclaration(method, {
+ currentClass: 'p5',
+ isInsideNamespace: true,
+ inGlobalMode: true
+ });
}
});
@@ -884,17 +992,27 @@ p5: P5;
globalDefinitions += '\n';
// Mirror typedef interfaces for global-mode usage
- const globalNamespaceOptions = { isInsideNamespace: true, inGlobalMode: true };
+ const globalNamespaceOptions = {
+ isInsideNamespace: true,
+ inGlobalMode: true
+ };
for (const [name, typedefEntry] of Object.entries(typedefs)) {
if (hasTypedefProperties(typedefEntry)) {
- globalDefinitions += generateTypedefInterface(name, typedefEntry, globalNamespaceOptions, 2);
+ globalDefinitions += generateTypedefInterface(
+ name,
+ typedefEntry,
+ globalNamespaceOptions,
+ 2
+ );
}
}
// Add all real classes as both types and constructors
Object.values(processed.classes).forEach(classData => {
if (classData.name !== 'p5') {
- const className = classData.name.startsWith('p5.') ? classData.name.substring(3) : classData.name;
+ const className = classData.name.startsWith('p5.')
+ ? classData.name.substring(3)
+ : classData.name;
// For Graphics, use __Graphics for constructor
if (className === 'Graphics') {
globalDefinitions += ` type ${className} = P5.${className};\n`;
@@ -935,13 +1053,23 @@ p5: P5;
// Also declare functions in global scope
globalP5Methods.forEach(method => {
- globalDefinitions += generateMethodDeclaration(method, { currentClass: 'p5', isInsideNamespace: true, inGlobalMode: true, globalFunction: true });
+ globalDefinitions += generateMethodDeclaration(method, {
+ currentClass: 'p5',
+ isInsideNamespace: true,
+ inGlobalMode: true,
+ globalFunction: true
+ });
});
// Add strands functions as global functions
strandsMethods.forEach(method => {
if (!conflictingDOMFunctions.includes(method.name)) {
- globalDefinitions += generateMethodDeclaration(method, { currentClass: 'p5', isInsideNamespace: true, inGlobalMode: true, globalFunction: true });
+ globalDefinitions += generateMethodDeclaration(method, {
+ currentClass: 'p5',
+ isInsideNamespace: true,
+ inGlobalMode: true,
+ globalFunction: true
+ });
}
});
@@ -955,10 +1083,13 @@ p5: P5;
const { instanceDefinitions, globalDefinitions } = generateTypeDefinitions();
fs.writeFileSync(path.join(__dirname, '../types/p5.d.ts'), instanceDefinitions);
-fs.writeFileSync(path.join(__dirname, '../types/global.d.ts'), globalDefinitions);
+fs.writeFileSync(
+ path.join(__dirname, '../types/global.d.ts'),
+ globalDefinitions
+);
console.log('TypeScript definitions generated successfully!');
// Apply patches
console.log('Applying TypeScript patches...');
-applyPatches();
\ No newline at end of file
+applyPatches();
diff --git a/visual-report.js b/visual-report.js
index 3a29e223cb..0f579dd676 100644
--- a/visual-report.js
+++ b/visual-report.js
@@ -4,15 +4,21 @@ const SLASH_REGEX = /\//g;
async function generateVisualReport() {
const expectedDir = path.join(process.cwd(), 'test/unit/visual/screenshots');
- const actualDir = path.join(process.cwd(), 'test/unit/visual/actual-screenshots');
- const outputFile = path.join(process.cwd(), 'test/unit/visual/visual-report.html');
-
+ const actualDir = path.join(
+ process.cwd(),
+ 'test/unit/visual/actual-screenshots'
+ );
+ const outputFile = path.join(
+ process.cwd(),
+ 'test/unit/visual/visual-report.html'
+ );
+
// Make sure the output directory exists
const outputDir = path.dirname(outputFile);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
-
+
// Function to read image file and convert to data URL
function imageToDataURL(filePath) {
try {
@@ -24,7 +30,7 @@ async function generateVisualReport() {
return null;
}
}
-
+
// Create a lookup map for actual screenshots
function createActualScreenshotMap() {
const actualMap = new Map();
@@ -32,33 +38,35 @@ async function generateVisualReport() {
console.warn(`Actual screenshots directory does not exist: ${actualDir}`);
return actualMap;
}
-
+
const files = fs.readdirSync(actualDir);
for (const file of files) {
if (file.endsWith('.png') && !file.endsWith('-diff.png')) {
actualMap.set(file, path.join(actualDir, file));
}
}
-
+
return actualMap;
}
-
+
const actualScreenshotMap = createActualScreenshotMap();
-
+
// Recursively find all test cases
function findTestCases(dir, prefix = '') {
const testCases = [];
-
+
if (!fs.existsSync(path.join(dir, prefix))) {
console.warn(`Directory does not exist: ${path.join(dir, prefix)}`);
return testCases;
}
-
- const entries = fs.readdirSync(path.join(dir, prefix), { withFileTypes: true });
-
+
+ const entries = fs.readdirSync(path.join(dir, prefix), {
+ withFileTypes: true
+ });
+
for (const entry of entries) {
const fullPath = path.join(prefix, entry.name);
-
+
if (entry.isDirectory()) {
// Recursively search subdirectories
testCases.push(...findTestCases(dir, fullPath));
@@ -66,42 +74,43 @@ async function generateVisualReport() {
// Found a test case
const metadataPath = path.join(dir, fullPath);
let metadata;
-
+
try {
metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
} catch (error) {
console.error(`Failed to read metadata: ${metadataPath}`, error);
continue;
}
-
+
const testDir = path.dirname(fullPath);
-
+
const test = {
name: testDir,
numScreenshots: metadata.numScreenshots || 0,
screenshots: []
};
-
+
// Create flattened name for lookup
const flattenedName = testDir.replace(SLASH_REGEX, '-');
-
+
// Collect all screenshots for this test
for (let i = 0; i < test.numScreenshots; i++) {
const screenshotName = i.toString().padStart(3, '0') + '.png';
const expectedPath = path.join(dir, testDir, screenshotName);
-
+
// Use flattened name for actual screenshots
const actualScreenshotName = `${flattenedName}-${i.toString().padStart(3, '0')}.png`;
- const actualPath = actualScreenshotMap.get(actualScreenshotName) || null;
-
+ const actualPath =
+ actualScreenshotMap.get(actualScreenshotName) || null;
+
// Use flattened name for diff image
const diffScreenshotName = `${flattenedName}-${i.toString().padStart(3, '0')}-diff.png`;
const diffPath = path.join(actualDir, diffScreenshotName);
-
+
const hasExpected = fs.existsSync(expectedPath);
const hasActual = actualPath && fs.existsSync(actualPath);
const hasDiff = fs.existsSync(diffPath);
-
+
const screenshot = {
index: i,
expectedImage: hasExpected ? imageToDataURL(expectedPath) : null,
@@ -109,41 +118,43 @@ async function generateVisualReport() {
diffImage: hasDiff ? imageToDataURL(diffPath) : null,
passed: hasExpected && hasActual && !hasDiff
};
-
+
test.screenshots.push(screenshot);
}
-
+
// Don't add tests with no screenshots
if (test.screenshots.length > 0) {
testCases.push(test);
}
}
}
-
+
return testCases;
}
-
+
// Find all test cases from the expected directory
const testCases = findTestCases(expectedDir);
-
+
if (testCases.length === 0) {
- console.warn('No test cases found. Check if the expected directory is correct.');
+ console.warn(
+ 'No test cases found. Check if the expected directory is correct.'
+ );
}
-
+
// Count passed/failed tests and screenshots
const totalTests = testCases.length;
let passedTests = 0;
let totalScreenshots = 0;
let passedScreenshots = 0;
-
+
for (const test of testCases) {
const testPassed = test.screenshots.every(screenshot => screenshot.passed);
if (testPassed) passedTests++;
-
+
totalScreenshots += test.screenshots.length;
passedScreenshots += test.screenshots.filter(s => s.passed).length;
}
-
+
// Generate HTML
const html = `
@@ -311,25 +322,28 @@ async function generateVisualReport() {
Summary
Total Tests: ${totalTests}
- Passed Tests: ${passedTests} (${totalTests > 0 ? Math.round(passedTests/totalTests*100) : 0}%)
- Failed Tests: ${totalTests - passedTests} (${totalTests > 0 ? Math.round((totalTests-passedTests)/totalTests*100) : 0}%)
+ Passed Tests: ${passedTests} (${totalTests > 0 ? Math.round((passedTests / totalTests) * 100) : 0}%)
+ Failed Tests: ${totalTests - passedTests} (${totalTests > 0 ? Math.round(((totalTests - passedTests) / totalTests) * 100) : 0}%)
Total Screenshots: ${totalScreenshots}
- Passed Screenshots: ${passedScreenshots} (${totalScreenshots > 0 ? Math.round(passedScreenshots/totalScreenshots*100) : 0}%)
+ Passed Screenshots: ${passedScreenshots} (${totalScreenshots > 0 ? Math.round((passedScreenshots / totalScreenshots) * 100) : 0}%)
Report Generated: ${new Date().toLocaleString()}