[Git] Fix conflicts

This commit is contained in:
Quentin
2020-03-27 21:16:47 +01:00
22 changed files with 281 additions and 211 deletions

View File

@@ -23,6 +23,7 @@ jobs:
submodules: true submodules: true
- run: make -j2 MODEL=n0100 epsilon.dfu - run: make -j2 MODEL=n0100 epsilon.dfu
- run: make -j2 MODEL=n0100 epsilon.onboarding.dfu - run: make -j2 MODEL=n0100 epsilon.onboarding.dfu
- run: make -j2 MODEL=n0100 epsilon.official.onboarding.dfu
- run: make -j2 MODEL=n0100 epsilon.onboarding.update.dfu - run: make -j2 MODEL=n0100 epsilon.onboarding.update.dfu
- run: make -j2 MODEL=n0100 epsilon.onboarding.beta.dfu - run: make -j2 MODEL=n0100 epsilon.onboarding.beta.dfu
- run: make -j2 MODEL=n0100 flasher.light.dfu - run: make -j2 MODEL=n0100 flasher.light.dfu
@@ -42,6 +43,7 @@ jobs:
submodules: true submodules: true
- run: make -j2 epsilon.dfu - run: make -j2 epsilon.dfu
- run: make -j2 epsilon.onboarding.dfu - run: make -j2 epsilon.onboarding.dfu
- run: make -j2 epsilon.official.onboarding.dfu
- run: make -j2 epsilon.onboarding.update.dfu - run: make -j2 epsilon.onboarding.update.dfu
- run: make -j2 epsilon.onboarding.beta.dfu - run: make -j2 epsilon.onboarding.beta.dfu
- run: make -j2 flasher.light.dfu - run: make -j2 flasher.light.dfu
@@ -63,10 +65,11 @@ jobs:
with: with:
submodules: true submodules: true
- run: make -j2 PLATFORM=simulator TARGET=web - run: make -j2 PLATFORM=simulator TARGET=web
- run: make -j2 PLATFORM=simulator TARGET=web epsilon.official.js
- uses: actions/upload-artifact@master - uses: actions/upload-artifact@master
with: with:
name: epsilon-simulator-web.zip name: epsilon-simulator-web.zip
path: output/release/simulator/web/simulator.zip path: output/release/simulator/web/epsilon.zip
- run: make -j2 PLATFORM=simulator TARGET=web test.headless.js - run: make -j2 PLATFORM=simulator TARGET=web test.headless.js
build-simulator-linux: build-simulator-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -21,9 +21,6 @@ OMEGA_THEME ?= omega_light
ifndef USE_LIBA ifndef USE_LIBA
$(error platform.mak should define USE_LIBA) $(error platform.mak should define USE_LIBA)
endif endif
ifndef EXE
$(error platform.mak should define EXE, the extension for executables)
endif
include build/toolchain.$(TOOLCHAIN).mak include build/toolchain.$(TOOLCHAIN).mak
SFLAGS += -DDEBUG=$(DEBUG) SFLAGS += -DDEBUG=$(DEBUG)

View File

@@ -1,8 +1,10 @@
TOOLCHAIN = android TOOLCHAIN = android
EXE = so
EPSILON_TELEMETRY ?= 1 EPSILON_TELEMETRY ?= 1
ifdef NDK_ABI ARCHS = armeabi-v7a arm64-v8a x86 x86_64
BUILD_DIR := $(BUILD_DIR)/$(NDK_ABI)
ifdef ARCH
EXE = so
BUILD_DIR := $(BUILD_DIR)/$(ARCH)
endif endif

View File

@@ -1,8 +1,15 @@
# Define standard compilation rules # Define standard compilation rules
.PHONY: official_authorization .PHONY: official_authorization
ifeq ($(ACCEPT_OFFICIAL_TOS),1)
official_authorization: official_authorization:
@echo "CAUTION: You are about to build an official NumWorks firmware. Distribution of such firmware by a third party is prohibited. Are you sure you want to proceed? Please type "yes" to confirm." && read ans && [ $${ans:-no} = yes ] else
official_authorization:
@echo "CAUTION: You are trying to build an official NumWorks firmware."
@echo "Distribution of such firmware by a third party is prohibited."
@echo "Please set the ACCEPT_OFFICIAL_TOS environment variable to proceed."
@exit -1
endif
$(eval $(call rule_for, \ $(eval $(call rule_for, \
AS, %.o, %.s, \ AS, %.o, %.s, \
@@ -31,6 +38,12 @@ $(eval $(call rule_for, \
$$(CXX) $$(CXXFLAGS) $$(SFLAGS) -c $$< -o $$@ \ $$(CXX) $$(CXXFLAGS) $$(SFLAGS) -c $$< -o $$@ \
)) ))
$(eval $(call rule_for, \
CPP, %, %.inc, \
$$(CPP) -P $$< $$@ \
))
ifdef EXE
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
# Work around command-line length limit # Work around command-line length limit
# On Msys2 the max command line is 32 000 characters. Our standard LD command # On Msys2 the max command line is 32 000 characters. Our standard LD command
@@ -47,6 +60,7 @@ $(eval $(call rule_for, \
$$(LD) $$^ $$(LDFLAGS) -o $$@ \ $$(LD) $$^ $$(LDFLAGS) -o $$@ \
)) ))
endif endif
endif
$(eval $(call rule_for, \ $(eval $(call rule_for, \
WINDRES, %.o, %.rc, \ WINDRES, %.o, %.rc, \

View File

@@ -40,18 +40,16 @@ all_official:
$(Q) $(MAKE) MODEL=n0100 epsilon.official.onboarding.dfu $(Q) $(MAKE) MODEL=n0100 epsilon.official.onboarding.dfu
$(Q) cp output/release/device/n0100/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0100.dfu $(Q) cp output/release/device/n0100/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0100.dfu
$(Q) echo "BUILD_FIRMWARE SIMULATOR WEB ZIP" $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB ZIP"
$(Q) $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web clean $(Q) $(MAKE) PLATFORM=simulator TARGET=web clean
$(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web output/release/simulator/web/simulator.official.zip $(Q) $(call source_emsdk); $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.zip
$(Q) cp output/release/simulator/web/simulator.official.zip output/all_official/simulator.web.zip $(Q) cp output/release/simulator/web/epsilon.official.zip output/all_official/simulator.web.zip
$(Q) echo "BUILD_FIRMWARE SIMULATOR WEB JS" $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB JS"
$(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web epsilon.official.js $(Q) $(call source_emsdk); $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.js
$(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.js $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.js
$(Q) cp output/release/simulator/web/epsilon.official.js.mem output/all_official/epsilon.js.mem
$(Q) echo "BUILD_FIRMWARE SIMULATOR WEB PYTHON JS" $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB PYTHON JS"
$(Q) $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web clean $(Q) $(MAKE) PLATFORM=simulator TARGET=web clean
$(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web EPSILON_GETOPT=1 EPSILON_APPS=code epsilon.official.js $(Q) $(call source_emsdk); $(MAKE) PLATFORM=simulator TARGET=web EPSILON_GETOPT=1 EPSILON_APPS=code epsilon.official.js
$(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.python.js $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.python.js
$(Q) cp output/release/simulator/web/epsilon.official.js.mem output/all_official/epsilon.python.js.mem
$(Q) echo "BUILD_FIRMWARE SIMULATOR ANDROID" $(Q) echo "BUILD_FIRMWARE SIMULATOR ANDROID"
$(Q) $(MAKE) PLATFORM=simulator TARGET=android clean $(Q) $(MAKE) PLATFORM=simulator TARGET=android clean
$(Q) $(MAKE) PLATFORM=simulator TARGET=android epsilon.official.signed.apk $(Q) $(MAKE) PLATFORM=simulator TARGET=android epsilon.official.signed.apk

View File

@@ -0,0 +1,3 @@
ifndef ARCH
HANDY_TARGETS_EXTENSIONS += apk
endif

View File

@@ -1,6 +1,3 @@
$(BUILD_DIR)/epsilon%packed.js: EMSCRIPTEN_INIT_FILE = 0 HANDY_TARGETS_EXTENSIONS += zip
$(BUILD_DIR)/test.headless.js: EMSCRIPTEN_MODULARIZE = 0 $(BUILD_DIR)/test.headless.js: EMSCRIPTEN_MODULARIZE = 0
$(BUILD_DIR)/epsilon.packed.js: $(call object_for,$(epsilon_src))
$(BUILD_DIR)/epsilon.official.packed.js: $(call object_for,$(epsilon_official_src))

View File

@@ -13,16 +13,16 @@ NDK_TOOLCHAIN_PATH = $(NDK_PATH)/toolchains/llvm/prebuilt/$(NDK_HOST_TAG)/bin
# is no toolchain for those archs on those API levels. Let's enforce NDK_VERSION # is no toolchain for those archs on those API levels. Let's enforce NDK_VERSION
# at 21 for these archs, and 16 for the others. # at 21 for these archs, and 16 for the others.
ifeq ($(NDK_ABI),armeabi-v7a) ifeq ($(ARCH),armeabi-v7a)
NDK_TARGET = armv7a-linux-androideabi NDK_TARGET = armv7a-linux-androideabi
NDK_VERSION = 16 NDK_VERSION = 16
else ifeq ($(NDK_ABI),arm64-v8a) else ifeq ($(ARCH),arm64-v8a)
NDK_TARGET = aarch64-linux-android NDK_TARGET = aarch64-linux-android
NDK_VERSION = 21 NDK_VERSION = 21
else ifeq ($(NDK_ABI),x86) else ifeq ($(ARCH),x86)
NDK_TARGET = i686-linux-android NDK_TARGET = i686-linux-android
NDK_VERSION = 16 NDK_VERSION = 16
else ifeq ($(NDK_ABI),x86_64) else ifeq ($(ARCH),x86_64)
NDK_TARGET = x86_64-linux-android NDK_TARGET = x86_64-linux-android
NDK_VERSION = 21 NDK_VERSION = 21
endif endif

View File

@@ -1,6 +1,7 @@
CC = emcc CC = emcc
CXX = emcc CXX = emcc
LD = emcc LD = emcc
CPP = cpp
EMSCRIPTEN_ASYNC_SYMBOLS = \ EMSCRIPTEN_ASYNC_SYMBOLS = \
SAFE_HEAP_LOAD \ SAFE_HEAP_LOAD \
@@ -120,20 +121,9 @@ endif
# Configure EMFLAGS # Configure EMFLAGS
EMFLAGS += -s WASM=0 EMFLAGS += -s WASM=0
# Since emcc 1.39.5, DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR is defautly
# to 1 which discards old looks up to find DOM elements. However SDL relies on
# the presence of a target "#canvas" that used to return the Module['canvas']
# target but not anymore. It also expects the existence of Module['canvas'].
# Until we fix the DOM of the html calling epsilon module, we use the deprecated
# 'find_event_target'.
# TODO: fix DOM of htmls files that uses epsilon js module
EMFLAGS += -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0
# Configure LDFLAGS # Configure LDFLAGS
EMSCRIPTEN_MODULARIZE ?= 1 EMSCRIPTEN_MODULARIZE ?= 1
LDFLAGS += -s MODULARIZE=$(EMSCRIPTEN_MODULARIZE) -s 'EXPORT_NAME="Epsilon"' LDFLAGS += -s MODULARIZE=$(EMSCRIPTEN_MODULARIZE) -s 'EXPORT_NAME="Epsilon"' --memory-init-file 0
EMSCRIPTEN_INIT_FILE ?= 1
LDFLAGS += --memory-init-file $(EMSCRIPTEN_INIT_FILE)
SFLAGS += $(EMFLAGS) SFLAGS += $(EMFLAGS)
LDFLAGS += $(EMFLAGS) -Oz -s EXPORTED_FUNCTIONS='["_main", "_IonSimulatorKeyboardKeyDown", "_IonSimulatorKeyboardKeyUp", "_IonSimulatorEventsPushEvent", "_IonSoftwareVersion", "_IonPatchLevel", "_IonDisplayForceRefresh"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["UTF8ToString"]' LDFLAGS += $(EMFLAGS) -Oz -s EXPORTED_FUNCTIONS='["_main", "_IonSimulatorKeyboardKeyDown", "_IonSimulatorKeyboardKeyUp", "_IonSimulatorEventsPushEvent", "_IonSoftwareVersion", "_IonPatchLevel", "_IonDisplayForceRefresh"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["UTF8ToString"]'

View File

@@ -1,8 +1,5 @@
#include <escher/run_loop.h> #include <escher/run_loop.h>
#include <assert.h> #include <assert.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
RunLoop::RunLoop() : RunLoop::RunLoop() :
m_time(0) { m_time(0) {

View File

@@ -11,6 +11,12 @@ $(call object_for,ion/src/simulator/shared/main.cpp) : SFLAGS += -DEPSILON_SDL_F
LDFLAGS += -ljnigraphics -llog LDFLAGS += -ljnigraphics -llog
# If ARCH is not defined, we will re-trigger a build for each avaialble ARCH.
# This is used to build APKs, which needs to embbed a binary for each ARCH.
ifndef ARCH
# Android resources # Android resources
# Some android resources needs to be filtered through ImageMagick. Others are # Some android resources needs to be filtered through ImageMagick. Others are
# simply copied over. # simply copied over.
@@ -27,56 +33,50 @@ $(BUILD_DIR)/app/res/%.xml: ion/src/simulator/android/src/res/%.xml | $$(@D)/.
$(call rule_label,COPY) $(call rule_label,COPY)
$(Q) cp $< $@ $(Q) cp $< $@
# Cross-ABI libepsilon.so # This rule allow us to build any executable (%) for a specified ARCH ($1)
# This file is loaded is loaded only once, which prevents us from tracking # We depend on a phony target to make sure this rule is always executed
# dependencies across ABIs. As a shortcut, we simply force a re-make of
# libepsilon.so for each ABI.
.PHONY: force_remake .PHONY: force_remake
define rule_for_arch_executable
define rule_for_libepsilon .PRECIOUS: $$(BUILD_DIR)/$(1)/%.so
$$(BUILD_DIR)/app/libs/%/lib$(1): force_remake $$$$(@D)/. $$(BUILD_DIR)/$(1)/%.so: force_remake
$(Q) echo "MAKE NDK_ABI=$$*" $(Q) echo "MAKE ARCH=$(1) $$*.so"
$(Q) $$(MAKE) NDK_ABI=$$* $(1) $(Q) $$(MAKE) ARCH=$(1) --silent $$*.so
$(Q) cp $$(BUILD_DIR)/$$*/$(1) $$@
endef endef
$(eval $(call rule_for_libepsilon,epsilon.so)) # We need to put the .so files somewhere Gradle can pick them up.
$(eval $(call rule_for_libepsilon,epsilon.official.so)) # We decided to use the location "app/libs/$EXECUTABLE/$ARCH/libepsilon.so"
# This way it's easy to import the shared object from Java code (it's always
# named libepsilon.so), and it's easy to make Gradle use a given executable by
# simply using the jniLibs.src directive.
define path_for_arch_jni_lib
$$(BUILD_DIR)/app/libs/%/$(1)/libepsilon.so
endef
# If NDK_ABI is not defined, we will re-trigger a build for each avaialble ABI. define rule_for_arch_jni_lib
# This is used to build APKs, which needs to embbed a binary for each ABI. $(call path_for_arch_jni_lib,$(1)): $$(BUILD_DIR)/$(1)/%.so | $$$$(@D)/.
$(Q) cp $$< $$@
endef
ifndef NDK_ABI $(foreach ARCH,$(ARCHS),$(eval $(call rule_for_arch_executable,$(ARCH))))
$(foreach ARCH,$(ARCHS),$(eval $(call rule_for_arch_jni_lib,$(ARCH))))
NDK_ABIS = armeabi-v7a arm64-v8a x86 x86_64 apk_deps = $(foreach ARCH,$(ARCHS),$(call path_for_arch_jni_lib,$(ARCH)))
apk_deps += $(subst ion/src/simulator/android/src/res,$(BUILD_DIR)/app/res,$(wildcard ion/src/simulator/android/src/res/*/*))
apk_deps += $(addprefix $(BUILD_DIR)/app/res/,mipmap/ic_launcher.png mipmap-v26/ic_launcher_foreground.png)
epsilon_apk_deps = $(subst ion/src/simulator/android/src/res,$(BUILD_DIR)/app/res,$(wildcard ion/src/simulator/android/src/res/*/*)) .PRECIOUS: $(apk_deps)
epsilon_apk_deps += $(addprefix $(BUILD_DIR)/app/res/,mipmap/ic_launcher.png mipmap-v26/ic_launcher_foreground.png)
define rule_for_gradle $(BUILD_DIR)/%.apk: $(apk_deps)
.PHONY: gradle_$1_$2
gradle_$1_$2: $$(epsilon_apk_deps) $$(patsubst %,$$(BUILD_DIR)/app/libs/%/libepsilon$2so,$(NDK_ABIS))
@echo "GRADLE ion/src/simulator/android/build.gradle" @echo "GRADLE ion/src/simulator/android/build.gradle"
$(Q) ANDROID_HOME=$(ANDROID_HOME) EPSILON_VERSION=$(EPSILON_VERSION) BUILD_DIR=$(BUILD_DIR) ion/src/simulator/android/gradlew -b ion/src/simulator/android/build.gradle $1 $(Q) ANDROID_HOME=$(ANDROID_HOME) EPSILON_VERSION=$(EPSILON_VERSION) BUILD_DIR=$(BUILD_DIR) EPSILON_VARIANT=$* ion/src/simulator/android/gradlew -b ion/src/simulator/android/build.gradle assembleRelease
endef $(Q) cp $(BUILD_DIR)/app/outputs/apk/release/android-release*.apk $@
$(eval $(call rule_for_gradle,assembleCodesigned,.))
$(eval $(call rule_for_gradle,assembleRelease,.))
$(eval $(call rule_for_gradle,assembleCodesigned,.official.))
$(eval $(call rule_for_gradle,assembleRelease,.official.))
DEFAULT = epsilon.apk DEFAULT = epsilon.apk
.PHONY: epsilon%signed.apk .PHONY: %_run
epsilon%signed.apk: gradle_assembleCodesigned_% %_run: $(BUILD_DIR)/%.apk
$(warning This is a signed build.) @echo "ADB $*.apk"
$(Q) adb install $<
.PHONY: epsilon%apk
epsilon%apk: gradle_assembleRelease_%
$(warning Building without code signing. Build epsilon$*signed.apk to generate a signed version.)
.PHONY: epsilon_run
epsilon_run: gradle_installDebug
endif endif

View File

@@ -53,10 +53,9 @@ android {
release { release {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt') proguardFiles getDefaultProguardFile('proguard-android.txt')
} if (projectVariable('SIGNING_STORE_FILE')) {
codesigned { signingConfig signingConfigs.environment
initWith buildTypes.release }
signingConfig signingConfigs.environment
} }
} }
sourceSets{ sourceSets{
@@ -64,12 +63,13 @@ android {
manifest.srcFile 'src/AndroidManifest.xml' manifest.srcFile 'src/AndroidManifest.xml'
res.srcDir BUILD_DIR + '/res' res.srcDir BUILD_DIR + '/res'
java.srcDir 'src' java.srcDir 'src'
jniLibs.srcDir BUILD_DIR + '/libs' jniLibs.srcDir BUILD_DIR + '/libs/' + System.getenv('EPSILON_VARIANT')
assets.srcDir '../assets' assets.srcDir '../assets'
} }
} }
lintOptions { lintOptions {
abortOnError false abortOnError false
checkReleaseBuilds false
} }
} }

View File

@@ -10,6 +10,9 @@ SFLAGS += -Iion/src/simulator/web/include
# Only render the screen, not the whole calculator which will be drawn in HTML # Only render the screen, not the whole calculator which will be drawn in HTML
SFLAGS += -DEPSILON_SDL_SCREEN_ONLY=1 SFLAGS += -DEPSILON_SDL_SCREEN_ONLY=1
# Enable to set environment variables for a module
LDFLAGS += --pre-js ion/src/simulator/web/preamble_env.js
ion_src += $(addprefix ion/src/simulator/web/, \ ion_src += $(addprefix ion/src/simulator/web/, \
callback.cpp \ callback.cpp \
helpers.cpp \ helpers.cpp \
@@ -24,14 +27,14 @@ ion_src += ion/src/simulator/shared/dummy/telemetry_init.cpp
ion_src += ion/src/shared/telemetry_console.cpp ion_src += ion/src/shared/telemetry_console.cpp
endif endif
DEFAULT = $(BUILD_DIR)/simulator.zip DEFAULT = epsilon.zip
$(BUILD_DIR)/simulator%zip: $(BUILD_DIR)/epsilon%packed.js $(BUILD_DIR)/epsilon%zip: $(BUILD_DIR)/epsilon%js $(BUILD_DIR)/ion/src/simulator/web/simulator.html
@rm -rf $(basename $@) @rm -rf $(basename $@)
@mkdir -p $(basename $@) @mkdir -p $(basename $@)
@cp $^ $(basename $@)/epsilon.js @cp $< $(basename $@)/epsilon.js
@cp ion/src/simulator/assets/background.jpg $(basename $@)/ @cp ion/src/simulator/assets/background.jpg $(basename $@)/
@cp ion/src/simulator/web/simulator.html $(basename $@)/ @cp $(BUILD_DIR)/ion/src/simulator/web/simulator.html $(basename $@)/
$(call rule_label,ZIP) $(call rule_label,ZIP)
@zip -r -9 -j $@ $(basename $@) > /dev/null @zip -r -9 -j $@ $(basename $@) > /dev/null
@rm -rf $(basename $@) @rm -rf $(basename $@)

View File

@@ -0,0 +1,21 @@
/* Use:
*
* var options = {
canvas: ...,
env: {
VARIABLE_NAME: VARIABLE_VALUE
}
};
Epsilon(options);
*
* Cf https://github.com/emscripten-core/emscripten/issues/9827
* */
Module["preRun"] = function () {
if (Module['env'] && typeof Module['env'] === 'object') {
for (var key in Module['env']) {
if (Module['env'].hasOwnProperty(key))
ENV[key] = Module['env'][key];
}
}
}

View File

@@ -0,0 +1,54 @@
<div id="keyboard">
<div class="nav">
<span class="left" data-key="0"></span>
<span class="top" data-key="1"></span>
<span class="bottom" data-key="2"></span>
<span class="right" data-key="3"></span>
<span class="ok" data-key="4"></span>
<span class="back" data-key="5"></span>
<span class="home" data-key="6"></span>
<span class="power" data-key="7"></span>
</div>
<div class="functions">
<span data-key="12"></span>
<span data-key="13"></span>
<span data-key="14"></span>
<span data-key="15"></span>
<span data-key="16"></span>
<span data-key="17"></span>
<span data-key="18"></span>
<span data-key="19"></span>
<span data-key="20"></span>
<span data-key="21"></span>
<span data-key="22"></span>
<span data-key="23"></span>
<span data-key="24"></span>
<span data-key="25"></span>
<span data-key="26"></span>
<span data-key="27"></span>
<span data-key="28"></span>
<span data-key="29"></span>
</div>
<div class="digits">
<span data-key="30"></span>
<span data-key="31"></span>
<span data-key="32"></span>
<span data-key="33"></span>
<span data-key="34"></span>
<span data-key="36"></span>
<span data-key="37"></span>
<span data-key="38"></span>
<span data-key="39"></span>
<span data-key="40"></span>
<span data-key="42"></span>
<span data-key="43"></span>
<span data-key="44"></span>
<span data-key="45"></span>
<span data-key="46"></span>
<span data-key="48"></span>
<span data-key="49"></span>
<span data-key="50"></span>
<span data-key="51"></span>
<span data-key="52"></span>
</div>
</div>

View File

@@ -0,0 +1,65 @@
/* To use this file you should adhere to the following conventions:
* - Main canvas has id #canvas
* - Secondary canvas (fullscreen on the side) has id #secondary-canvas
* - Calculator keyboard has id #keyboard */
var Module;
(function () {
var mainCanvas = document.getElementById('canvas');
var secondaryCanvasContext = document.getElementById('secondary-canvas').getContext('2d');
var epsilonLanguage = document.documentElement.lang || window.navigator.language.split('-')[0];
Module = {
canvas: mainCanvas,
arguments: ['--language', epsilonLanguage],
onDisplayRefresh: function() {
secondaryCanvasContext.drawImage(mainCanvas, 0, 0);
}
}
Epsilon(Module);
document.querySelectorAll('#keyboard span').forEach(function(span){
function eventHandler(keyHandler) {
return function(ev) {
var key = this.getAttribute('data-key');
keyHandler(key);
/* Always prevent default action of event.
* First, this will prevent the browser from delaying that event. Indeed
* the browser would otherwise try to see if that event could have any
* other meaning (e.g. a click) and might delay it as a result.
* Second, this prevents touch events to be handled twice. Indeed, for
* backward compatibility reasons, mobile browsers usually create a fake
* mouse event after each real touch event. This allows desktop websites
* to work unmodified on mobile devices. But here we are explicitly
* handling both touch and mouse events. We therefore need to disable
* the default action of touch events, otherwise the handler would get
* called twice. */
ev.preventDefault();
};
}
/* We decide to hook both to touch and mouse events
* On most mobile browsers, mouse events are generated if addition to touch
* events, so this could seem pointless. But those mouse events are not
* generated in real time: instead, they are buffered and eventually fired
* in a very rapid sequence. This prevents Epsilon from generating an event
* since this quick sequence will trigger the debouncer. */
['touchstart', 'mousedown'].forEach(function(type){
span.addEventListener(type, eventHandler(Module._IonSimulatorKeyboardKeyDown));
});
['touchend', 'mouseup'].forEach(function(type){
span.addEventListener(type, eventHandler(Module._IonSimulatorKeyboardKeyUp));
});
});
}());
function screenshot() {
// toDataURL needs the canvas to be refreshed
Module._IonDisplayForceRefresh();
var canvas = document.getElementById('canvas');
var link = document.createElement('a');
link.download = 'screenshot.png';
link.href = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
link.click();
}

View File

@@ -1,3 +1,5 @@
#define KEYBOARD #keyboard
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
@@ -65,7 +67,7 @@ a.action svg {
max-height: 92.0vh; max-height: 92.0vh;
max-width: 100%; max-width: 100%;
display: block; } display: block; }
.calculator .screen { .calculator #canvas {
position: absolute; position: absolute;
top: 8.5%; top: 8.5%;
left: 16.25%; left: 16.25%;
@@ -84,84 +86,84 @@ a.action svg {
display: inline-block; display: inline-block;
margin-right: 10px; margin-right: 10px;
} }
.calculator .keyboard { KEYBOARD {
position: absolute; position: absolute;
top: 38.5%; top: 38.5%;
left: 10%; left: 10%;
width: 81%; width: 81%;
bottom: 5%; } bottom: 5%; }
.calculator .keyboard span { KEYBOARD span {
cursor: pointer; cursor: pointer;
border-radius: 40%; border-radius: 40%;
display: block; display: block;
float: left; } float: left; }
.calculator .keyboard span:hover { KEYBOARD span:hover {
background-color: rgba(0, 0, 0, 0.1); } background-color: rgba(0, 0, 0, 0.1); }
.calculator .keyboard span:active { KEYBOARD span:active {
background-color: rgba(0, 0, 0, 0.2); } background-color: rgba(0, 0, 0, 0.2); }
.calculator .keyboard .nav { KEYBOARD .nav {
position: absolute; position: absolute;
top: 0; top: 0;
height: 27%; height: 27%;
width: 100%; } width: 100%; }
.calculator .keyboard .nav span { KEYBOARD .nav span {
position: absolute; } position: absolute; }
.calculator .keyboard .nav .left { KEYBOARD .nav .left {
top: 36%; top: 36%;
left: 2%; left: 2%;
width: 15%; width: 15%;
height: 28%; } height: 28%; }
.calculator .keyboard .nav .right { KEYBOARD .nav .right {
top: 36%; top: 36%;
left: 17%; left: 17%;
width: 15%; width: 15%;
height: 28%; } height: 28%; }
.calculator .keyboard .nav .top { KEYBOARD .nav .top {
top: 7%; top: 7%;
left: 12%; left: 12%;
width: 10%; width: 10%;
height: 40%; } height: 40%; }
.calculator .keyboard .nav .bottom { KEYBOARD .nav .bottom {
top: 53%; top: 53%;
left: 12%; left: 12%;
width: 10%; width: 10%;
height: 40%; } height: 40%; }
.calculator .keyboard .nav .home { KEYBOARD .nav .home {
top: 15%; top: 15%;
left: 41%; left: 41%;
width: 16%; width: 16%;
height: 33%; } height: 33%; }
.calculator .keyboard .nav .power { KEYBOARD .nav .power {
top: 54%; top: 54%;
left: 42%; left: 42%;
width: 16%; width: 16%;
height: 33%; } height: 33%; }
.calculator .keyboard .nav .ok { KEYBOARD .nav .ok {
top: 31%; top: 31%;
left: 67%; left: 67%;
width: 13%; width: 13%;
height: 38%; } height: 38%; }
.calculator .keyboard .nav .back { KEYBOARD .nav .back {
top: 31%; top: 31%;
left: 84%; left: 84%;
width: 13%; width: 13%;
height: 38%; } height: 38%; }
.calculator .keyboard .functions { KEYBOARD .functions {
position: absolute; position: absolute;
top: 26.75%; top: 26.75%;
left: 0.5%; left: 0.5%;
width: 98%; } width: 98%; }
.calculator .keyboard .functions span { KEYBOARD .functions span {
margin: 1.7% 1%; margin: 1.7% 1%;
width: 14.65%; width: 14.65%;
height: 0; height: 0;
padding-top: 10%; } padding-top: 10%; }
.calculator .keyboard .digits { KEYBOARD .digits {
position: absolute; position: absolute;
top: 56.5%; top: 56.5%;
left: 0.5%; left: 0.5%;
width: 98%; } width: 98%; }
.calculator .keyboard .digits span { KEYBOARD .digits span {
margin: 1.8% 2%; margin: 1.8% 2%;
width: 16%; width: 16%;
height: 0; height: 0;
@@ -173,61 +175,8 @@ a.action svg {
<div class="col-calculator"> <div class="col-calculator">
<div class="calculator"> <div class="calculator">
<img src="background.jpg" alt="NumWorks Calculator"> <img src="background.jpg" alt="NumWorks Calculator">
<canvas id="screen" class="screen" oncontextmenu="event.preventDefault()"></canvas> <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<div class="keyboard"> #include "simulator-keyboard.html"
<div class="nav">
<span class="left" data-key="0"></span>
<span class="top" data-key="1"></span>
<span class="bottom" data-key="2"></span>
<span class="right" data-key="3"></span>
<span class="ok" data-key="4"></span>
<span class="back" data-key="5"></span>
<span class="home" data-key="6"></span>
<span class="power" data-key="7"></span>
</div>
<div class="functions">
<span data-key="12"></span>
<span data-key="13"></span>
<span data-key="14"></span>
<span data-key="15"></span>
<span data-key="16"></span>
<span data-key="17"></span>
<span data-key="18"></span>
<span data-key="19"></span>
<span data-key="20"></span>
<span data-key="21"></span>
<span data-key="22"></span>
<span data-key="23"></span>
<span data-key="24"></span>
<span data-key="25"></span>
<span data-key="26"></span>
<span data-key="27"></span>
<span data-key="28"></span>
<span data-key="29"></span>
</div>
<div class="digits">
<span data-key="30"></span>
<span data-key="31"></span>
<span data-key="32"></span>
<span data-key="33"></span>
<span data-key="34"></span>
<span data-key="36"></span>
<span data-key="37"></span>
<span data-key="38"></span>
<span data-key="39"></span>
<span data-key="40"></span>
<span data-key="42"></span>
<span data-key="43"></span>
<span data-key="44"></span>
<span data-key="45"></span>
<span data-key="46"></span>
<span data-key="48"></span>
<span data-key="49"></span>
<span data-key="50"></span>
<span data-key="51"></span>
<span data-key="52"></span>
</div>
</div>
<div class="actions"> <div class="actions">
<a id="action-fullscreen" class="action"> <a id="action-fullscreen" class="action">
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g fill="#434343"><path d="M5.075,6.95 L6.918,5.088 L3.938,2.062 L5.955,0.018 L0.052,0.018 L0.052,6.004 L2.098,3.928 L5.075,6.95 Z" class="si-glyph-fill"></path><path d="M16.0034788,9.916 L13.832,12.013 L10.799,8.96 L8.918,10.841 L11.957,13.897 L9.961,15.9813842 L16.0034788,15.9813842 L16.0034788,9.916 Z"></path></g></g></svg> <svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g fill="#434343"><path d="M5.075,6.95 L6.918,5.088 L3.938,2.062 L5.955,0.018 L0.052,0.018 L0.052,6.004 L2.098,3.928 L5.075,6.95 Z" class="si-glyph-fill"></path><path d="M16.0034788,9.916 L13.832,12.013 L10.799,8.96 L8.918,10.841 L11.957,13.897 L9.961,15.9813842 L16.0034788,15.9813842 L16.0034788,9.916 Z"></path></g></g></svg>
@@ -239,46 +188,14 @@ a.action svg {
</div> </div>
</div> </div>
<div class="col-fullscreen"> <div class="col-fullscreen">
<canvas class="screen-magnified" width="320" height="240"></canvas> <canvas id="secondary-canvas" width="320" height="240"></canvas>
</div> </div>
</div> </div>
<script src="epsilon.js"></script> <script src="epsilon.js"></script>
<script> <script>
var mainCanvas = document.querySelector(".calculator .screen"); #include "simulator-setup.js"
var secondaryCanvasContext = document.querySelector(".screen-magnified").getContext("2d");
var Module = {
arguments: ["--language", window.navigator.language.split('-')[0]],
canvas: mainCanvas,
onDisplayRefresh: function() {
secondaryCanvasContext.drawImage(mainCanvas, 0, 0);
}
};
Epsilon(Module);
</script> </script>
<script> <script>
function screenshot() {
<!--toDataURL needs the canvas to be refreshed-->
Module._IonDisplayForceRefresh();
var canvas = document.querySelector('.screen');
var link = document.createElement('a');
link.download = 'screenshot.png';
link.href = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
link.click();
}
var spans = document.querySelectorAll(".calculator .keyboard span");
for (var i=0; i< spans.length; i++) {
var span = spans[i];
span.addEventListener("mousedown", function(e) {
Module._IonSimulatorKeyboardKeyDown(this.getAttribute("data-key"));
});
span.addEventListener("mouseup", function(e) {
Module._IonSimulatorKeyboardKeyUp(this.getAttribute("data-key"));
});
}
document.getElementById("action-fullscreen").addEventListener("click", function(e){ document.getElementById("action-fullscreen").addEventListener("click", function(e){
if (document.body.className == "fullscreen") { if (document.body.className == "fullscreen") {
document.body.className = ""; document.body.className = "";

View File

@@ -10,7 +10,7 @@ namespace Poincare {
namespace ApproximationHelper { namespace ApproximationHelper {
template <typename T> int PositiveIntegerApproximationIfPossible(const ExpressionNode * expression, bool * isUndefined, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); template <typename T> int PositiveIntegerApproximationIfPossible(const ExpressionNode * expression, bool * isUndefined, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
template <typename T> std::complex<T> NeglectRealOrImaginaryPartIfNeglectable(std::complex<T> result, std::complex<T> input1, std::complex<T> input2 = 1.0); template <typename T> std::complex<T> NeglectRealOrImaginaryPartIfNeglectable(std::complex<T> result, std::complex<T> input1, std::complex<T> input2 = 1.0, bool enableNullResult = true);
template <typename T> using ComplexCompute = Complex<T>(*)(const std::complex<T>, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); template <typename T> using ComplexCompute = Complex<T>(*)(const std::complex<T>, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
template<typename T> Evaluation<T> Map(const ExpressionNode * expression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexCompute<T> compute); template<typename T> Evaluation<T> Map(const ExpressionNode * expression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexCompute<T> compute);

View File

@@ -37,7 +37,7 @@ template <typename T> int ApproximationHelper::PositiveIntegerApproximationIfPos
return absInt((int)scalar); return absInt((int)scalar);
} }
template <typename T> std::complex<T> ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::complex<T> result, std::complex<T> input1, std::complex<T> input2) { template <typename T> std::complex<T> ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::complex<T> result, std::complex<T> input1, std::complex<T> input2, bool enableNullResult) {
/* Cheat: openbsd functions (cos, sin, tan, cosh, acos, pow...) are /* Cheat: openbsd functions (cos, sin, tan, cosh, acos, pow...) are
* numerical implementation and thus are approximative. * numerical implementation and thus are approximative.
* The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to avoid * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to avoid
@@ -47,6 +47,9 @@ template <typename T> std::complex<T> ApproximationHelper::NeglectRealOrImaginar
* sin(1E-15)=1E-15. * sin(1E-15)=1E-15.
* We can't do that for all evaluation as the user can operate on values as * We can't do that for all evaluation as the user can operate on values as
* small as 1E-308 (in double) and most results still be correct. */ * small as 1E-308 (in double) and most results still be correct. */
if (!enableNullResult && (result.imag() == 0.0 || result.real() == 0.0)) {
return result;
}
T magnitude1 = minimalNonNullMagnitudeOfParts(input1); T magnitude1 = minimalNonNullMagnitudeOfParts(input1);
T magnitude2 = minimalNonNullMagnitudeOfParts(input2); T magnitude2 = minimalNonNullMagnitudeOfParts(input2);
T precision = 10.0*Expression::Epsilon<T>(); T precision = 10.0*Expression::Epsilon<T>();
@@ -126,8 +129,8 @@ template<typename T> MatrixComplex<T> ApproximationHelper::ElementWiseOnComplexM
template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible<float>(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible<float>(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit);
template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible<double>(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible<double>(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit);
template std::complex<float> Poincare::ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable<float>(std::complex<float>,std::complex<float>,std::complex<float>); template std::complex<float> Poincare::ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable<float>(std::complex<float>,std::complex<float>,std::complex<float>,bool);
template std::complex<double> Poincare::ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable<double>(std::complex<double>,std::complex<double>,std::complex<double>); template std::complex<double> Poincare::ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable<double>(std::complex<double>,std::complex<double>,std::complex<double>,bool);
template Poincare::Evaluation<float> Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute<float> compute); template Poincare::Evaluation<float> Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute<float> compute);
template Poincare::Evaluation<double> Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute<double> compute); template Poincare::Evaluation<double> Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute<double> compute);
template Poincare::Evaluation<float> Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction<float> computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction<float> computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction<float> computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction<float> computeOnMatrices); template Poincare::Evaluation<float> Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction<float> computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction<float> computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction<float> computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction<float> computeOnMatrices);

View File

@@ -321,18 +321,22 @@ void Expression::defaultDeepReduceChildren(ExpressionNode::ReductionContext redu
Expression Expression::defaultShallowReduce() { Expression Expression::defaultShallowReduce() {
Expression result; Expression result;
const int childrenCount = numberOfChildren(); if (sSimplificationHasBeenInterrupted) {
for (int i = 0; i < childrenCount; i++) { result = Undefined::Builder();
/* The reduction is shortcut if one child is unreal or undefined: } else {
* - the result is unreal if at least one child is unreal const int childrenCount = numberOfChildren();
* - the result is undefined if at least one child is undefined but no child for (int i = 0; i < childrenCount; i++) {
* is unreal */ /* The reduction is shortcut if one child is unreal or undefined:
ExpressionNode::Type childIType = childAtIndex(i).type(); * - the result is unreal if at least one child is unreal
if (childIType == ExpressionNode::Type::Unreal) { * - the result is undefined if at least one child is undefined but no child
result = Unreal::Builder(); * is unreal */
break; ExpressionNode::Type childIType = childAtIndex(i).type();
} else if (childIType == ExpressionNode::Type::Undefined) { if (childIType == ExpressionNode::Type::Unreal) {
result = Undefined::Builder(); result = Unreal::Builder();
break;
} else if (childIType == ExpressionNode::Type::Undefined) {
result = Undefined::Builder();
}
} }
} }
if (!result.isUninitialized()) { if (!result.isUninitialized()) {

View File

@@ -183,7 +183,7 @@ Complex<T> PowerNode::compute(const std::complex<T> c, const std::complex<T> d,
* so arg(c^d) = y*ln(r)+xθ. * so arg(c^d) = y*ln(r)+xθ.
* We consider that arg[π] is negligeable if it is negligeable compared to * We consider that arg[π] is negligeable if it is negligeable compared to
* norm(d) = sqrt(x^2+y^2) and ln(r) = ln(norm(c)).*/ * norm(d) = sqrt(x^2+y^2) and ln(r) = ln(norm(c)).*/
return Complex<T>::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c, d)); return Complex<T>::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c, d, false));
} }
// Layout // Layout

View File

@@ -1317,6 +1317,8 @@ QUIZ_CASE(poincare_simplification_mix) {
//assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)×log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9) //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)×log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9)
assert_parsed_expression_simplify_to("(((√(6)-√(2))/4)/((√(6)+√(2))/4))+1", "-√(3)+3"); assert_parsed_expression_simplify_to("(((√(6)-√(2))/4)/((√(6)+√(2))/4))+1", "-√(3)+3");
assert_parsed_expression_simplify_to("1/√(𝐢) × (√(2)-𝐢×√(2))", "-2×𝐢"); // TODO: get rid of complex at denominator? assert_parsed_expression_simplify_to("1/√(𝐢) × (√(2)-𝐢×√(2))", "-2×𝐢"); // TODO: get rid of complex at denominator?
assert_expression_simplifies_approximates_to<double>("abs(√(300000.0003^23))", "9.7027409010183ᴇ62");
} }
QUIZ_CASE(poincare_hyperbolic_trigonometry) { QUIZ_CASE(poincare_hyperbolic_trigonometry) {