65.9K
CodeProject 正在变化。 阅读更多。
Home

Sumerics

starIconstarIconstarIconstarIconstarIcon

5.00/5 (23投票s)

2012年10月8日

CPOL

23分钟阅读

viewsIcon

67789

为最新一代超极本创建支持触控的传感器数值应用

Sumerics logo

引言

在本文中,我们将介绍支持触控的数值计算环境的初始创建步骤,即一个针对支持触控的计算设备进行了优化的环境。该应用程序名为 Sumerics,是传感器数值的缩写。该程序的核心是开源项目 YAMP,它基本上是一个(高度可扩展的)数值数学解析器和解释器。

该应用程序有两个主要目标:

  1. 一个直观的用户界面,用于对数据进行数值操作,并针对触摸屏和传感器进行了优化。
  2. 直接访问各种格式文件中的数据和实时传感器数据作为计算输入。

我们希望尽可能多地利用传感器数据,但这不应限制用户只能使用此类数据。用户应该能够导入自己的数据(来自已保存的二进制文件、图像、纯文本文件或 CSV 格式的文本文件),修改它,并用它进行计算。应用程序不应强制用户获取传感器数据,尽管这将是该应用程序的特色之一。

通过该应用程序,我们希望呈现数据的实时可视化。如果用户选择一个标量,我们将显示其在高斯平面中的点位置,以及一些统计数据,例如绝对值和相位(对于复数),或者整数的一些有趣属性,例如前一个和后一个质数、其质因数,或者更普遍地,该数字在当前工作区中的重要性。如果用户选择矩阵或向量,我们将根据维度显示绝对值的直方图,或2D或3D图。如果标记了两个已保存的变量,应用程序会尝试找出它们之间的一些关系。在某些情况下,最可能的关系是 x-y 图。否则,也会显示直方图。

可视化不止于此。我们利用设备的方向和环境光传感器,将 3D 图嵌入到设备的真实环境中。然而,最好的是,所有图都可以实时生成。这起初听起来毫无用处,但我们稍后将解释图形函数编辑器,它可以生成输出,然后用于最终评估,以(3D)图的形式呈现。

我们将讨论为 YAMP 编写插件所需的步骤,该插件将扩展现有函数库,使其包含特殊的传感器函数。这些函数通过桌面应用程序的传感器 API 直接连接到系统/超极本的硬件传感器。

特点

我们首先讨论需要处理的传感器

  • 加速度计:acc()
  • 环境光传感器:light()
  • 指南针:comp()
  • 设备方向:orient()
  • 陀螺仪:gyro()
  • 倾角计:inc()
  • GPS定位:gps()

不带参数的函数调用总是返回当前值。一些函数可能具有更多参数,用于切换某种模式或仅返回可能返回值的子集。我们以一种方式实现了所有函数,使它们已经过文档化并为最终用户提供示例。因此,当使用 help() 函数时,YAMP 将为我们完成显示适当帮助所需的工作。

Sumerics 的另一个重要部分是用户界面。实际上,用户界面是 Sumerics 与其他教育/数值应用程序不同的地方。一方面,我们不想重复造轮子;另一方面,使用一些经典但有用的功能非常重要。因此,我们也使用了三个基石:

  • 包含所有当前变量的工作区视图
  • 包含最近输入的命令和输出的历史视图
  • 用于输入新命令的文本框

除了这些经典选择之外,我们还添加了一个新的功能框,其中显示了常用/最近使用的功能。该列表可以轻松搜索或过滤(工作区也一样)。此外,我们还添加了一个可用传感器视图,它简化了访问,并以漂亮的实时图表显示当前值。

为了给我们的应用程序一些深度(同时不忽视触控输入可能性),我们添加了一个全景控制。从这一点开始,我们可以轻松地用许多额外的工具、视图和可能性来扩展我们的应用程序。其中一种可能性是图形函数编辑器,我们稍后将讨论。

背景

最近,关于开源项目 YAMP 的文章已经发表(在 CodeProject 上可用)。该项目目前处于预发布状态,因此一些功能仍然缺失或不完整,但是,由于我们同时负责 Sumerics 和 YAMP,所以 Sumerics 所需的且应包含在 YAMP 核心中的功能可以立即实现。

YAMP 的创建考虑了两个基本理念

  1. 提供一个可扩展且功能强大的数学解析器,它是开源的,并完全用 C# 编写。
  2. 使用数学解析器来生成一个可以通过命令行和触控友好的图形用户界面轻松控制的数值应用程序。

第二个想法演变为为最新的英特尔超极本一代开发一款产品,该产品可以使用触摸输入、鼠标和/或键盘。起初,我们不会包含纯命令行交互的可能性,但这将来可能会实现。

最终产品不仅会显示传感器数据并包含各种传感器的信息功能,它还将从不同视图中的传感器中受益。显然,方向方面用于确定窗口的最佳大小和位置。加速度计、方向以及环境光传感器将用于以一种新鲜、令人印象深刻的方式显示绘图。

编写应用程序

我们首先看一下全貌(绿色是第三方库,橙色是我们自己的外部库,蓝色是新的库/应用程序)

Structure of Sumerics

因此,我们创建了一个名为 Sumerics 的 WPF 应用程序,编写了两个利用 YAMP 的库,其中一个库还使用了超极本上的不同传感器。然后,该应用程序利用 YAMP 并将插件连接到 YAMP,这将使用户能够更好地利用 YAMP。

因此,Sumerics 的架构严重依赖于 YAMP。然而,YAMP 的一些函数对于终端用户来说应该比纯粹的(核心)YAMP 提供的更容易编写。让我们看一个这样的调用示例。如果用户想要清除某些变量,他或她将不得不输入以下命令

clear("a", "b", "c")

这将清除 3 个变量 abc。Sumerics 现在可以使用以下命令:

clear a b c

这是可行的,因为 Sumerics 也有一个命令解析器。如果命令解析器找到一个有效命令,Sumerics 将执行它。否则,整个表达式将传递给 YAMP,YAMP 将解析并解释该表达式。内置命令可以是任何内容,从更改 GUI(例如清除命令历史记录)到封装 YAMP 函数。

编写这样一个命令解析器非常容易,而且可以很快完成。我们所需要的只是一个抽象基类。在这种情况下,我们创建一个名为 YCommand 的类。我们将根据需要对 YCommand 进行几种实现。最后,我们将编写一个使用反射来注册预解析器(或者我们称之为命令解析器)可以使用的方法的函数。

注册方法就像以下代码片段一样简单:

public static void RegisterCommands()
{
    var lib = Assembly.GetExecutingAssembly();
    var types = lib.GetTypes();

    foreach (var type in types)
    {
        if (type.IsAbstract)
            continue;

        if (type.IsSubclassOf(typeof(YCommand)))
        {
            var cmd = type.GetConstructor(Type.EmptyTypes).Invoke(null) as YCommand;
            commands.Add(cmd.Name.ToLower(), cmd);
        }
    }
}

下面显示了使某些函数可从命令解析器获得的一小部分类。

Class diagram for the commands

命令解析器可处理任意数量的参数。基本代码与 YAMP 文章中提供的代码非常相似,其中 ArgumentFunction 类提供了类似的方法。

为了在我们的 YAMP 插件中编写特定的传感器函数,我们首先从一个抽象基类开始。这个类将简化我们的编码,因为它减少了重复编写相同的代码。此外,我们只需扩展其功能,即可为所有派生类提供更多可能性。让我们看一下 acc() 函数的实现。

using YAMP;
using Microsoft.WindowsAPICodePack.Sensors;

namespace YAMP.Sensors
{
    public class AccFunction : SensorFunction
    {
        private static Accelerometer3D _accelerometer;

        static AccFunction()
        {
            if (!Manager.Initialized || _accelerometer != null)
                return;

            try
            {
                SensorList<Accelerometer3D> sl = 
                       SensorManager.GetSensorsByTypeId<Accelerometer3D>();
                if (sl.Count > 0)
                {
                    _accelerometer = sl[0];
                }
            }
            catch (SensorPlatformException e)
            {
                System.Diagnostics.Debug.WriteLine(e.Message);
            }
            Exists = _accelerometer != null;
        }

        public static AccelerometerReading Measurement
        {
            get
            {
                if (!Manager.Initialized || !Exists)
                    return null;

                AccelerometerReading reading = new AccelerometerReading(
                    _accelerometer.CurrentAcceleration[AccelerationAxis.XAxis],
                    _accelerometer.CurrentAcceleration[AccelerationAxis.YAxis],
                    _accelerometer.CurrentAcceleration[AccelerationAxis.ZAxis]);
                return reading;
            }
        }

        public MatrixValue Function()
        {
            if (!Manager.Initialized || !Exists)
                return new MatrixValue(3, 1);

            var reading = Measurement;
            var result = new MatrixValue(3, 1);
            result[1].Value = reading.AccelerationX;
            result[2].Value = reading.AccelerationY;
            result[3].Value = reading.AccelerationZ;

            return result;
        }
    }

    public class AccelerometerReading
    {
        public double AccelerationX { get; private set; }
        public double AccelerationY { get; private set; }
        public double AccelerationZ { get; private set; }

        public AccelerometerReading(double AccelerationX, 
                                    double AccelerationY, double AccelerationZ)
        {
            this.AccelerationX = AccelerationX;
            this.AccelerationY = AccelerationY;
            this.AccelerationZ = AccelerationZ;
        }
    }
}

这里我们提供了许多 static 函数,因此可以直接从 Sumerics 访问(即,用于传感器的实时数据),而无需创建显式实例或让 YAMP 解释固定表达式。我们将传感器的读取打包在一个小型嵌套类 AccelerometerReading 中,然后通过获取当前测量值返回该类。

如果传感器不存在,将返回 null 值,但 YAMP 始终返回数据。在这种情况下,YAMP 将返回具有正确维度的标量/矩阵,但没有任何值(因此每个值都将是 0)。

关注点

处理所有传感器相关的事情非常巧妙。起初,一切看起来都异常简单,但那是在我意识到传感器 API(托管)仅在 Windows 应用商店应用程序中可用之前。因此,如果我们要真正在 C# 中使用它们(我们确实想使用!),就必须使用一个封装非托管代码的包装器。

编写图形函数编辑器是一个相当大的挑战,因为它需要灵活轻巧,但也需要强大而稳健。在这里,我们必须添加使用键盘的可能性

  • 快捷方式
  • 方向键
  • 文本修改
  • 处理缩放

我们还必须连接鼠标事件。对于这个输入设备,我们将不得不处理以下操作列表

  • 拖放新项目
  • 连接线条
  • 修改项目和线条
  • 使用鼠标滚轮处理缩放和滚动

我们的应用程序完全支持触控。图形编辑器是 Sumerics 的核心部分之一,因此需要在此处实现许多不同的手势。

  • 用一根或两根手指连接两个点(创建一条线)
  • 用一根或三根手指断开两个点(删除一条线)
  • 用一根手指拖动新项目
  • 用两根手指放大和缩小
  • 用一根手指滑动以滚动

所有这些触控事件都必须精确地连接起来,不应适得其反,而应是可选的补充,即用户体验不依赖于多个输入设备,而是通过多个(不同的)输入设备变得更好(而不是变得更糟)。

教育目的

Example main view of Sumerics

我们计划包含大量样本工作表(用 YAMP 脚本编写),以提供与传感器配合使用的即用型实验。很容易想象,Sumerics 将能够进行一些“实时实验”,而无需实际设置任何组件——只需开箱即用,并使用超极本。因此,我们瞄准了教育机构,例如学校的高级物理课程或本科生的实验。

让我们看看计划实验清单上的一些项目

  • 测量(当前,即取决于位置的)地球磁场
  • 测量(当前,即取决于位置的)g因子
  • 方向与集成加速度计
  • 用于检测垂直角度的倾角计
  • 指南针与 GPS
  • 环境光传感器,用于测量特定环境中的光子

像 MATLAB、Mathematica 和其他科学程序这样的用户界面在触摸屏方面存在问题,并且没有考虑任何传感器数据。通过 YAMP,我们试图创建一个更现代的用户界面,通过为用户提供独特的触摸体验和更丰富的交互体验,来扩展标准 PC 的可能性。

目标读者

我们的目标受众目前是高等教育或大学的研究人员或学生。Sumerics 的主要目的是使用数值数学或利用提供的传感器数据进行数值实验,从而创建开箱即用的实验。

我们的目标受众对技术高度敏感,可能会将超极本作为日常设备,用于可视化、创建和发挥创意。因此,Sumerics 很有可能通过购买一些硬件并插入即可轻松支持更多传感器。我们可能会编写一个非常通用的传感器函数,可以直接接近驱动程序进行访问。

更多传感器!

除了超极本独有的传感器之外,我们还将包含一些其他传感器,它们可能会提供有趣的数据,并可用于一些奇妙的实验。

首先,让我们看看将要包含的“明显”传感器

  • 鼠标(屏幕上的 X,Y 坐标)
  • 触摸(返回可用于 3D 绘图的数据,在当前触摸点处有峰值)

鼠标可用于进行机械拉动实验。一个简单的函数(借助用户输入)可以粗略地找出像素/厘米的比率。有了这些知识,您可以估算鼠标的速度(如果您知道像素/秒和像素/厘米,就可以计算出厘米/秒,从而得出米/秒和公里/小时等)。

触摸输入可用于演示触摸信号的工作原理。它还允许用户更多地了解其触摸屏的特性。

最后,我们还将把声卡端口作为传感器。原因如下:

  • 它可以很好地展示快速傅里叶变换(FFT)的强大功能。
  • 它允许用户对声音输入进行光谱分析。
  • 可以轻松创建自己的传感器,将(模拟)信号调制到(数字)声音输入,然后由 Sumerics 接收和使用。

和当地电子实验室的一些人交流后,这听起来(!)像个很棒的主意。基本上,你可以用声音输入做很多有用的事情。通过函数将其连接到其他传感器,可以为(虚拟和真实的)实验奠定强大的基础。一个可能的例子是:通过声音输入的峰值功率谱和加速度计,测量尖叫和过山车行驶中当前重力之间的关系。

这个概念还有很多可能性。我们将在 Sumerics 发布时附带的工作表集中包含一些示例。

在超极本上测试

该应用程序需要在英特尔超极本上进行测试。至少可以说,我真的很兴奋,迫不及待地想拥有一台超极本,以便让这个(在我看来很棒的)应用程序有机会变得真正稳定。

测试将侧重于整体用户体验和获取正确的传感器数据。它还将关注不同的测试用例,例如,如果传感器未返回有效数据或传感器不可用怎么办。

实际情况如何?

比赛似乎有足够的时间,但十一月(和一些有趣的会议)有大量的任务要处理,严重打乱了我的计划。尽管如此,通过连续 3 天每天编码约 16 小时,我还是能够及时提交应用程序。

因此,当我看到屏幕上的以下部分时,我松了一口气。

Sumerics Submission

话虽如此,让我们深入了解 Sumerics——目前的状况和未来的计划。

应用程序的外观

提交到英特尔 AppUp 商店的截图展示了我的设计目标。主视图(称为交互)是控制台所在的位置,也是大多数用户交互发生的地方。

Sumerics Interaction Mainview

当然,此视图会(很可能)严重偏向键盘交互,这就是为什么里面有两个关键点。第一个是集成在屏幕上的键盘,当用户按下键盘按钮时,它会向上滑动。此键盘只有这一种模式,足以输入任何语句。这些语句可以包含矩阵、虚数(因此 i 已放置在键盘上的数字旁边)以及所有可能的运算符。

Sumerics Interaction Touchmode

第二件事是“绘制表达式”按钮,它会打开微软的数学输入面板。用户完成表达式绘制后,Sumerics 能够获取表达式并将其输入到控制台。Sumerics 甚至可以毫无问题地处理矩阵和分数的输入。

第二个截图还显示了函数的过滤。目前,这些函数只是文档,但将来,它们也可能用于拖放或其他有用的操作。这样,用户需要键入的字符更少(使用屏幕键盘或实体键盘)。

一个非常重要的部分是绘图部分。这里显示的是最新(选择或创建,以最后一次动作为准)的绘图。所有修改都可以在控制台模式下完成,但是,为了保持触控友好,最常见的设置已提供给图形用户界面和触控用户。所有这些设置都可以通过按下底部(命令)栏上的按钮来访问。

Sumerics polar plot

用户可以将图表保存为 PNG 文件以导出。他还可以打印显示的图表,或修改包含的系列属性或一般应用的设置。为方便起见,网格线开关已创建为按钮。通过中间按钮,图表可以重置为其默认状态。这很重要,因为图表可以通过触摸操作进行缩放和平移。最后一个按钮打开一个迷你控制台,它只是一个小输入窗口。此输入窗口可用于手动设置其他设置或创建整个图表。此对话框的目的是最大限度地减少用户在控制台输入和图表输出之间切换的方式。

图形脚本是不得不暂时放弃的东西。大部分已经实现,但有些地方仍待完善,还有大量的测试工作需要完成。脚本看起来就像下面的截图:

Sumerics scripting

在此视图中,用户基本上(通过触摸或鼠标)拖放元素类别。之后,他可以调整元素(在此截图中,我们放置了单独的语句框——这些框只有一个 textbox。通常,会显示 combobox 或一些更高级的对话框)。

顶层节点是输入和输出元素。在截图中,没有添加输入元素(它们将是紫色的,位于左侧),这导致一个没有任何参数的函数。该链由两个语句块组成,第一个语句块没有任何输入,将其输出提供给第二个框。第二个语句块将其输出定向到脚本函数的输出。在这种情况下,我们只会得到 13 * 29。

我们还创建了一个小型关于对话框,其中包含用户的 GPS 位置(作为一个彩蛋)。通常,指南针和 GPS 数据也位于传感器图表部分——但由于 GPS 数据变化非常缓慢,将其放置在关于对话框中更有意义。

Sumerics about

“关于”对话框显示 GPS 和指南针数据。在最新版本中,数据也绑定到 GPS 和指南针传感器的当前状态(定期更新)。

应用程序设计

函数、矩阵等的图标是我自己创建的。所有其他图标均来自 Austin Andrews 制作的精美 Modern UI 图标集。有数百个图标,足以满足大多数用例。应用程序主题由 MAHApps Metro 界面提供。

大多数控件也取自这个库。然而,无论是 MAHApps Metro 还是 .NET-Framework 都不包含一个适合 WPF 的漂亮触控友好型颜色选择器。因此,我只是自己编写了一个。我还搜索了一个不错的控制台控件,但我没有找到任何合适的。所以我最终采用了 Pavel Torgashov 的 Fast Colored TextBox 进行语法高亮显示并对其进行了修改。实际上,我删除了大部分代码并专门针对我的目的进行了修改。控件的主要渲染仍然在 GDI+ 中完成,因为我发现移植它可能可行,但可能需要太多时间。我还完全重写了自动完成部分——这次是在 WPF 中。新代码大约只有原来的 1/20 左右,并且完全在 WPF 中渲染。

幕后

该代码包含多个使用 C# 5 强大功能的 async 语句。为了充分利用强大的 WPF 绑定引擎,除了自定义控制台之外,整个应用程序中仅使用了 MVVM 原则。该控制台实际上是一个 Windows Forms 控件,即一个派生自 Windows Forms Control 类并具有自定义 (GDI+) 绘图的类。最终,使用现有(在此情况下为 Windows Forms)解决方案并对其进行修改,结果比从头开始要快得多!

以下(主要)视图模型已创建

  • Main
  • 绘图设置
  • 绘图系列
  • 绘图系列元素
  • 文档
  • 帮助主题
  • 帮助磁贴
  • 选项
  • 变量
  • 查询结果

还实现了一些其他(微小的)视图模型。这些视图模型只是为了支持控件、对话框或更大的视图模型。最终,它被证明是正确的解决方案,因为我们只需复制粘贴即可实现以下扩展:在两个不同的选项卡中复制变量栏。

这听起来微不足道,但实际上它是一个很好的测试,可以判断一个应用程序是否从程序员的角度进行了良好设计。现在一切似乎都已同步——正如它应该的那样。

总而言之,Sumerics 的性能相当不错,即使是大型绘图也能快速显示。这要归功于开源项目 Oxyplot 的开发人员做出了出色的工作。唯一不完美的是他们平移和缩放的实现。我原以为他们没有包含一些东西——但他们确实包含了。唯一的缺点是它的性能非常差。缩放和平移需要几秒钟到半分钟(取决于数据大小)。这比重新绘制整个轴线变化的东西要长得多。我搜索了阻止内置平移和缩放(并实现自己的)的可能性,但我找不到这样的可能性。因此,我最终只能寄希望于他们能够更新解决这个问题。

接下来,我将写几行关于代码中一些有趣的部分。它们并非革命性或以任何方式必需的,但它们让应用程序的编程变得轻松许多。以下代码片段在各种场合都使用过:

public static T GetWindow<T>() where T : Window, new()
{
    foreach (Window window in App.Current.Windows)
    {
        if (window is T)
        {
            window.Activate();
            return (T)window;
        }
    }

    var win = new T();
    win.Show();
    return win;
}

因此,基本上,可以使用此方法获取任意 Window。如果对话框已打开,它将被带到前台。否则,将创建并显示一个新实例。

通常,您希望根据属性在 XAML 中触发某些操作。有时,您会考虑到绑定来执行此操作,但如果绑定的值可能为 null 怎么办?此外,如果您想实际考虑在值为 null(或反之不是 null)时触发呢?以下转换器是关键:

public class IsNullConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value == null;
    }

    public object ConvertBack(object value, Type targetType, 
                              object parameter, CultureInfo culture)
    {
        throw new InvalidOperationException("IsNullConverter can only be used OneWay.");
    }
}

在 XAML 中使用转换器或多或少是直接的。只需要以下几行(假设您使用 local 作为您的“真实”(即 C#)命名空间的 XML 命名空间):

<!-- In the resources (e.g. use in app.xaml or elsewhere) -->
<ResourceDictionary>     
    <local:IsNullConverter x:Key="inc"/>
</ResourceDictionary>

<!-- Now use a DataTrigger like this -->
<DataTrigger Binding="{Binding WhatAreYouBindingTo?, 
 Converter={StaticResource inc}}" Value="True">
<!-- Place what should happen here ... -->
</DataTrigger>

为了在各种场合测试一些按键,我需要大量的重复代码。我对这种情况不满意,决定编写一些扩展方法。结果如下:

public static bool IsCtrl(this KeyEventArgs keyEvent, Key value)
{
    return keyEvent.KeyboardDevice.Modifiers == ModifierKeys.Control && keyEvent.Key == value;
}

public static bool IsModified(this KeyEventArgs keyEvent, Key value)
{
    return keyEvent.KeyboardDevice.Modifiers != ModifierKeys.None && keyEvent.Key == value;
}

public static bool IsCtrlShift(this KeyEventArgs keyEvent, Key value)
{
    return keyEvent.KeyboardDevice.Modifiers == ModifierKeys.Control && 
      keyEvent.KeyboardDevice.Modifiers == ModifierKeys.Shift && keyEvent.Key == value;
}

public static bool Is(this KeyEventArgs keyEvent, Key value)
{
    return keyEvent.KeyboardDevice.Modifiers == ModifierKeys.None && keyEvent.Key == value;
}

这允许我直接测试具有特定修饰符的 KeyEventArgs 对象与特定键。

此外,另一个值得分享的代码片段是 Windows 数学输入面板中的 MathML 被发现并立即粘贴的部分。这是借助剪贴板处理程序实现的。不幸的是,这并没有内置到 .NET-Framework 中(我实际上不知道为什么——我认为这是一个非常有用的方法!)。

//Yes we will need those pointers ...
IntPtr viewerHandle = IntPtr.Zero;
IntPtr installedHandle = IntPtr.Zero;

//Two constants from the Win32 API
const int WM_DRAWCLIPBOARD = 0x308;
const int WM_CHANGECBCHAIN = 0x30D;

//Get the required API calls

[DllImport("user32.dll")]
private extern static IntPtr SetClipboardViewer(IntPtr hWnd);

[DllImport("user32.dll")]
private extern static int ChangeClipboardChain(IntPtr hWnd, IntPtr hWndNext);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private extern static int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    var hwndSource = PresentationSource.FromVisual(this) as HwndSource;

    if (hwndSource != null)
    {
        installedHandle = hwndSource.Handle;
        viewerHandle = SetClipboardViewer(installedHandle);
        hwndSource.AddHook(new HwndSourceHook(this.hwndSourceHook));
    }
}

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
    ChangeClipboardChain(this.installedHandle, this.viewerHandle);
    int error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
    e.Cancel = error != 0;
    base.OnClosing(e);
}

protected override void OnClosed(EventArgs e)
{
    this.viewerHandle = IntPtr.Zero;
    this.installedHandle = IntPtr.Zero;
    base.OnClosed(e);
}

IntPtr hwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case WM_CHANGECBCHAIN:
            this.viewerHandle = lParam;

            if (this.viewerHandle != IntPtr.Zero)
                SendMessage(this.viewerHandle, msg, wParam, lParam);

            break;

        case WM_DRAWCLIPBOARD:
            ReadClipboardData();

            if (this.viewerHandle != IntPtr.Zero)
                SendMessage(this.viewerHandle, msg, wParam, lParam);

            break;
    }

    return IntPtr.Zero;
}

void ReadClipboardData()
{
    //MathML could be something else ...
    var stream = Clipboard.GetData("MathML") as Stream;

    if (stream != null)
    {
        //Do something with the stream!
    }
}

我们主要寻找这些 MathML 片段。一旦检测到,我们就会读取剪贴板并查看它是否确实是 MathML 类型。如果是这种情况,那么我们要么直接粘贴它,要么也运行查询。这只是设置 Sumerics 的问题。默认选项是在粘贴后运行查询。

更新!

我匆忙地在截止日期前提交了应用程序,但我有比初始版本更大的计划。然而,尽管我在 12 月 1 日之前未能实现所有功能,但我还是实现了一些非常重要的功能,这些功能在初始版本中被省略了。这些功能在 11 月 28 日提交验证后,已包含在 0.9.8 版本中(0.9.5 是最初发布的版本)。不幸的是,我怀疑这些更新能否及时完成。这实际上是一种耻辱,因为这意味着我的比赛提交将缺少以下功能:

  • 控制台模式下的自动完成
  • 控制台模式下的语法高亮
  • 改进的 YAMP 版本(例如,可以处理 lambda 表达式)
  • 改进的文档对话框
  • Sumerics 网页将显示在特殊对话框中
  • 改进了绘图区域
  • 更多选项
  • 扩展触控键盘
  • 修复了一些小错误

现在是时候谈谈 Sumerics 的价格了。实际上,目前它是免费的。但这可能不会持续太久。一旦 Sumerics 达到 1.0.0 版本,我很有可能会以 4.99 欧元左右的价格出售(肯定不会超过 5 欧元)。我这样做有几个原因。首先也是最重要的一点:这个项目从未旨在让我致富。其次:如果应用程序不是免费的,那么客户可以假定会提供一些支持——所以这对我有意义。如果人们愿意为此付费,我将修复一些错误并集成功能。

最后一个论点实际上非常好:AppUp 商店里有一些数学应用程序很糟糕(我的意思是:真的很糟糕!),而且售价更高。我一定会证明你可以创建一个更好的应用程序,而且价格更便宜。此外,我希望比主要竞争对手更便宜,这就是为什么我必须首先考察这些(可能的竞争对手)。

Sumerics about

上图显示了最新版本中的控制台。我们可以看到自动完成菜单已打开并有语法高亮。我们还可以看到大型矩阵默认是折叠的。这改善了概览,并确保只直接显示所需(或简短)的输出。

我们还看到了命令在行动。一些函数已被 Sumerics 转换为命令。这就是为什么用户除了输入 cd("..") 之外,还可以输入 cd .. 的原因。对于 pwd() 和其他数十个函数也是如此。

一些链接

这里有一些不错的链接

历史

  • v1.0.0 | 初始发布 | 2012年10月8日
  • v1.1.0 | 修正了一些错别字,增加了关于目标用户的部分 | 2012年10月9日
  • v1.1.1 | 修正了一些错别字 | 2012年10月9日
  • v1.1.2 | 修正了一些错别字 | 2012年10月10日
  • v1.2.0 | 增加了关于附加传感器的部分 | 2012年10月11日
  • v2.0.0 | 项目总结 | 2012年11月29日
  • v2.0.1 | 添加了一些链接 | 2012年11月29日
© . All rights reserved.