The Curl® language Charts package is built on
the
Shapes package. It
enables you to display data in charts and graphs. You need to put
the data into a
RecordSet before you use it in a
Chart. You can also use
ConnectedRecordSet to chart data
from a database server. See
Data Access for more information on using
RecordSet and
ConnectedRecordSet. Charts created with the Curl charts
package update the chart when data in the associated record set
changes.
The Curl Charts API attempts to use reasonable colors and create
appropriate axes and legends by default. The API also provides
means for you to customize these chart components.
Chart is the base class for charts. The charts API
provides the subclasses
LayeredChart and
PieChart.
LayeredChart is the chart type for charts that
plot data in two dimensions using X and Y coordinates. Different
types of charts, such as bar charts and line charts, are
implemented as
ChartLayer objects. You can add one or
more
ChartLayer objects to a
LayeredChart to
create more complex charts. The API provides these chart layers:
The following sections describe the types of chart layers.
LineLayer draws lines connecting the data points in the
data series. The following example plots the braking distance of
a typical automobile as a function of vehicle speed. The
example plots the following data series:
- Thinking distance: the distance traveled in the time it
takes the driver to react
- Braking distance: the distance traveled by the vehicle
after the brakes are applies
- Overall distance: the total distance required to stop
the vehicle
The line chart clearly shows the increase in braking distance
with increased vehicle speed.
The example also supplies a
ChartAxis with an
axis-label for the left axis. The default label for the axis is a
list of the names or captions of the
RecordSet fields
that supply data to the chart. In this case, the list of captions
is too long to fit in the 6cm height of the chart, which can cause
the chart to draw incorrectly. The supplied axis label fits and is
more informative. The left
ChartAxis is based on a
ChartDataSeries constructed from the field in the source
record set that contains the overall distance values. Because the
overall distance is the sum of the thinking and braking distances,
this field contains the largest distance values, making it the
best choice to determine the axis range. See
LayeredChart Axes for more
information on working with chart axes.
The
ChartAxis supplied for the bottom axis sets the
keyword
force-zero? to
false, which allows the
axis to begin at a numeric value that fits the plotted data. See
Controlling the Range of an
Axis. This axis also illustrates the nonlocal option
tick-label-auto-stagger?. Its default value is
true,
which places tick labels at two different levels as they are here.
| Example:
Line chart |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField "KPH", caption = "Kilometers/hour", domain = int},
{RecordField "TD", caption = "Thinking distance", domain = int},
{RecordField "BD", caption = "Braking distance", domain = int},
{RecordField "OD", caption = "Overall distance", domain = int}
},
{RecordData KPH = 32, TD = 6, BD = 6, OD = 12},
{RecordData KPH = 48, TD = 9, BD = 14, OD = 23},
{RecordData KPH = 64, TD = 12, BD = 24, OD = 36},
{RecordData KPH = 80, TD = 15, BD = 38, OD = 53},
{RecordData KPH = 97, TD = 18, BD = 55, OD = 73},
{RecordData KPH = 113, TD = 21, BD = 75, OD = 96}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 15cm,
height = 6cm,
left-axis =
{ChartAxis
{ChartDataSeries records, "OD"},
axis-label = "Distance (meters)"
},
bottom-axis =
{ChartAxis
{ChartDataSeries records, "KPH"},
force-zero? = false
},
{LineLayer
{ChartDataSeries records, "OD"},
{ChartDataSeries records, "BD"},
{ChartDataSeries records, "TD"}
}
}
}
{VBox
{RecordGrid
height = 4cm,
width = 13cm,
record-source = records
},
chart
}
| |
AreaLayer draws the area under the line defined by the
data values. The following example uses the same data set and data
series as the example for
LineLayer. Drawing the area
under the line emphasizes how the the shape of the curve for
Thinking distance differs from the others.
| Example:
Area chart |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField "KPH", caption = "Kilometers/hour", domain = int},
{RecordField "TD", caption = "Thinking distance", domain = int},
{RecordField "BD", caption = "Braking distance", domain = int},
{RecordField "OD", caption = "Overall distance", domain = int}
},
{RecordData KPH = 32, TD = 6, BD = 6, OD = 12},
{RecordData KPH = 48, TD = 9, BD = 14, OD = 23},
{RecordData KPH = 64, TD = 12, BD = 24, OD = 36},
{RecordData KPH = 80, TD = 15, BD = 38, OD = 53},
{RecordData KPH = 97, TD = 18, BD = 55, OD = 73},
{RecordData KPH = 113, TD = 21, BD = 75, OD = 96}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 15cm,
height = 6cm,
left-axis =
{ChartAxis
{ChartDataSeries records, "OD"},
axis-label = "Distance (meters)"
},
bottom-axis =
{ChartAxis
{ChartDataSeries records, "KPH"},
force-zero? = false
},
{AreaLayer
{ChartDataSeries records, "OD"},
{ChartDataSeries records, "BD"},
{ChartDataSeries records, "TD"}
}
}
}
{VBox
{RecordGrid
height = 4cm,
width = 13cm,
record-source = records
},
chart
}
| |
ScatterLayer renders the data as points in
two-dimensional space. A different
ScatterShape, as well
as a different color, distinguishes each data series.
The following example uses a scatter chart to plot the minimum,
maximum and mean daily temperature in Boston MA for the year
2006. The scatter plot presents this large volume of data in a way
that makes visual analysis easier.
| Example:
Scatter chart |
 |
{import * from CURL.GUI.CHARTS}
{let boston-temp:CsvRecordSet =
{CsvRecordSet
{url "../../default/support/boston-temp.csv"},
fields =
{RecordFields
{RecordField "EST", domain = {StandardDateDomain}},
{RecordField "MaxT-F", caption = "Max Temperature (F)", domain = int},
{RecordField "MeanT-F", caption = "Mean Temperature (F)", domain = int},
{RecordField "MinT-F", caption = "Min Temperature (F)", domain = int}
}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 20cm,
height = 15cm,
bottom-axis-parent =
{ShapeGroup
tick-label-rotation = 0deg,
tick-label-factory =
{proc {axis:ChartAxis, tick:ChartTick, tick-rotation:Angle}:any
{return
{format "%d/%d", tick.value.info.month, tick.value.info.day}
}
}
},
bottom-axis =
{GenericDataSeriesAxis
{ChartDataSeries boston-temp, "EST"},
axis-label = "2006"
},
left-axis =
{ChartAxis
{ChartDataSeries boston-temp, "MaxT-F"},
{ChartDataSeries boston-temp, "MinT-F"},
axis-label = "Temperature (F)"
},
{ScatterLayer
{ChartDataSeries boston-temp, "MaxT-F"},
{ChartDataSeries boston-temp, "MeanT-F"},
{ChartDataSeries boston-temp, "MinT-F"}
}
}
}
{VBox
halign = "center",
{bold Daily Maximum, Mean, and Minimum Temperature, Boston MA},
chart
}
| |
A
BubbleLayer enables you to display additional
information with each point on the chart. The position of each
point with respect to the x and y axes plots two data values, as
the points on a scatter chart do. The height and width of the
shape drawn at each point can plot an additional two data
values. The
primary-size-data determines the horizontal
dimension of each bubble, and the
secondary-size-data
determines the vertical. You can provide only
primary-size-data, in which case the values determine the overall
size of each bubble.
The following example plots monthly household electrical use for
the year 2006, with months on the x axis and Kilowatt hours used
on the y axis. The primary-size-data is the average
heating degree days for the month, and the secondary-size-data is the average cooling degree days for the
month. Heating and cooling degree days are temperature-based
indices that reflect energy demand for heating and cooling.
Try removing the secondary-size-data and executing the
example. Note that the data bubbles become circles of varying
diameter.
| Example:
Bubble Chart |
 |
{import * from CURL.GUI.CHARTS}
{include "../../default/support/electric-use-data.scurl"}
{VBox
{RecordGrid
height = 5cm,
record-source = records
},
{LayeredChart
width = 15cm,
height = 7.25cm,
{BubbleLayer
records,
"kWh-06",
scatter-shape = "ellipse",
x-axis-data =
{ChartDataSeries records, "Month"},
primary-size-data =
{ChartDataSeries records, "CDD-06"},
secondary-size-data =
{ChartDataSeries records, "HDD-06"}
}
}
}
| |
BarLayer renders the associated data as a bar chart. This
example charts data from the same record set used in the bubble
chart example. This chart plots monthly electric usage for the
years 2005 and 2006. The chart uses a
EnumeratedBarChartAxis for the x axis. This axis is specialized
for bar charts. It places a tick at the end of the axis, and
positions data points and tick labels between the ticks.
| Example:
Bar chart |
 |
{import * from CURL.GUI.CHARTS}
{include "../../default/support/electric-use-data.scurl"}
{let chart:LayeredChart =
{LayeredChart
width = 20cm,
height = 6cm,
left-axis =
{ChartAxis
{ChartDataSeries records, "kWh-05"},
axis-label = "Kilowatt hours"
},
{BarLayer
{ChartDataSeries records, "kWh-05"},
{ChartDataSeries records, "kWh-06"},
x-axis-data = {ChartDataSeries records, "Month"}
}
}
}
{value chart}
| |
| Example:
Restructured data and chart |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 10cm,
height = 6cm,
{BarLayer
stacking-mode = ChartStackingMode.stacked,
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"},
x-axis-data = {ChartDataSeries records, "Region"}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
chart
}
| |
The following example illustrates a simple
PieChart,
which contains one
PieSet. It charts a
ChartDataSeries for the
M1 RecordField. showing
January sales figures for each region as a percentage of the total
circle. The
label-data property of
PieSet provides
labels for the pie chart sections.
| Example:
Pie chart |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int}
},
{RecordData Region = "North", M1 = 100},
{RecordData Region = "East", M1 = 110},
{RecordData Region = "South", M1 = 140},
{RecordData Region = "West", M1 = 160}
}
}
{let chart:PieChart =
{PieChart
width = 10cm,
height = 6cm,
{PieSet
{ChartDataSeries records, "M1"},
label-data = {ChartDataSeries records, "Region"}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
{Fill height = 1cm},
{text Sales figures for January:},
{Fill height = 1cm},
chart
}
| |
You can add more than one
PieSet to a
PieChart,
in a process somewhat analogous to adding multiple chart layers to
a layered chart. The result is a chart with the first
PieSet at the center of the chart, and additional pie sets
arranged in concentric rings in the order in which they were
added. The property
pie-set-inner-margin sets
the spacing between rings. The property
PieChart.inner-radius sets the radius of a circle at the center
of the inner-most
PieSet that changes it to a ring.
The following example adds a
PieSet for February sales
data, and another for March.
| Example:
Charting more than one PieSet |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let chart:PieChart =
{PieChart
width = 10cm,
height = 6cm,
pie-set-inner-margin = 1mm,
{PieSet {ChartDataSeries records, "M1"}},
{PieSet {ChartDataSeries records, "M2"}},
{PieSet
{ChartDataSeries records, "M3"},
label-data = {ChartDataSeries records, "Region"}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
{Fill height = 1cm},
{spaced-vbox
{text Sales figures for:},
{text January (inner ring), February (middle ring),
March (outer ring)}
},
{Fill height = 1cm},
chart
}
| |
The charts API creates charts from data in a
RecordSet. The data is likely to originate from a database
server, where it is probably stored in a format consistent with
good database design practice. This format may not be the most
appropriate for creating useful charts. This section discusses how
you can best structure you data to create effective and useful
charts.
For example, first quarter sales data for four geographic regions
might be stored in a database structured like this:
| Example:
Database data structure |
 |
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField
"Month",
domain = int
},
{RecordField
"Value",
domain = int
}
},
{RecordData Region = "North", Month = 1, Value = 100},
{RecordData Region = "North", Month = 2, Value = 140},
{RecordData Region = "North", Month = 3, Value = 130},
{RecordData Region = "East", Month = 1, Value = 110},
{RecordData Region = "East", Month = 2, Value = 140},
{RecordData Region = "East", Month = 3, Value = 170},
{RecordData Region = "South", Month = 1, Value = 140},
{RecordData Region = "South", Month = 2, Value = 100},
{RecordData Region = "South", Month = 3, Value = 130},
{RecordData Region = "West", Month = 1, Value = 160},
{RecordData Region = "West", Month = 2, Value = 190},
{RecordData Region = "West", Month = 3, Value = 140}
}
}
{RecordGrid height = 4cm, width = 10cm, record-source = records}
| |
Given this data structure, you can create data series containing
all the regions, all the months, or all the sales values; but none
of these series is very useful in a chart. What you probably want
is series containing all the values for January, all the values
for February, and all the values for March. You can achieve this
easily by restructuring the data as illustrated in the next
example. Once you have restructured the data, you can easily
generate a chart of monthly sales figures versus region.
| Example:
Restructured data and chart |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 10cm,
height = 6cm,
{BarLayer
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"},
x-axis-data = {ChartDataSeries records, "Region"}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
chart
}
| |
When you plan a project involving charts, you need to carefully
consider the data you are working with, it how that data is
structured. You also need to consider the type of data analysis
you need the charts to achieve. Then you can determine the
structure you need for the record sets that provide data to your
charts, and can write code to perform the necessary conversions.
ChartAxis is the abstract base class for the classes that
create axes on a
LayeredChart. These three subclasses of
ChartAxis define the basic axis types:
- DefaultChartAxis is the most simple implementation of
ChartAxis. It lays out the records, one after
another, in the order they appear in the record set. If space
allows, each record has a tick, labeled with a number
indicating position in the record set. If necessary, DefaultChartAxis reduces the number of ticks evenly, by
skipping every other tick, two out of three ticks and so on.
- DefaultBarChartAxis is almost identical to DefaultChartAxis, except that it formats the axis for bar
charts by generating an extra tick at the end of the axis and
positioning the data points and labels between ticks rather
than at them.
- NumericAxis-of uses a specified type and value range to
create a numeric axis.
The following example illustrates the difference between
subclasses of
DataSeriesAxis and
ChartAxis. As
it is initially loaded, the chart uses
NumericAxis-of for
its Y axis. If you change a data value in the source record set,
the change does not cause recalculation of the axis. For instance,
change the value for "February" in the region "South" from 100 to
300. Note that the axis does not change, and the corresponding bar
simply goes off the chart.
Now, comment this line:
left-axis = {new {NumericAxis-of double}, 0, 200},
and remove the comments from these lines:
left-axis = {new {NumericDataSeriesAxis-of double},
{ChartDataSeries records, "M2"}
},
| Example:
Difference between DataSeriesAxis
and ChartAxis |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField "Region", domain = String},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 10cm,
height = 6cm,
left-axis = {new {NumericAxis-of double}, 0, 200},
||-- left-axis = {new {NumericDataSeriesAxis-of double},
||-- {ChartDataSeries records, "M2"}
||-- },
{BarLayer
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"},
x-axis-data = {ChartDataSeries records, "Region"}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
chart
}
| |
When you create these axes, you must supply at least one
ChartDataSeries, and you can supply many. They must all be of the
same
Type with the same
Domain.
| Example:
Positioning chart axes |
 |
{import * from CURL.GUI.CHARTS}
{include "../../default/support/electric-use-data.scurl"}
{LayeredChart
width = 15cm,
height = 7.25cm,
left-axis = {ChartAxis
{ChartDataSeries records, "kWh-06"}
},
right-axis = {ChartAxis
{ChartDataSeries records, "CDD-06"},
{ChartDataSeries records, "HDD-06"}
},
{LineLayer
records,
"kWh-06",
x-axis-data =
{ChartDataSeries records, "Month"},
{ChartDataSeries records, "CDD-06"},
{ChartDataSeries records, "HDD-06"}
}
}
| |
The property
LayeredChart.flipped? reverses the
orientation of the X and Y axes. In a flipped chart, the X axis is
vertical, and the y axis is horizontal, as the following example
illustrates.
| Example:
Flipping the axes |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 10cm,
height = 6cm,
flipped? = true,
{BarLayer
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"},
x-axis-data = {ChartDataSeries records, "Region"}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
chart
}
| |
If you chart the example quarterly sales data as a line chart, all
the lines at the top of the chart, because the Y axis shows values
from zero to 200, but all the data values are above 100. The class
NumericDataSeriesAxis-of enables you to adjust the range
of values used on a chart axis. By default, the property
force-zero? is
true, which adjusts the axis values so
that the
zero value appears on the chart. Setting
force-zero? to
false, allows the chart to generate an
axis that provides the best fit for the charted values, as
illustrated in the following example.
Three additional properties enables you to exert more control over
range of values on a chart axis. If force-range? is true, then the chart uses forced-min and forced-max as the low and high values of the axis.
The three lines that are commented out in the following example
use these properties to set the axis range from 80 to
200. Remove the comments and execute the example to see
how they change the axis.
| Example:
Adjusting the range of an axis |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 10cm,
height = 6cm,
left-axis = {new {NumericDataSeriesAxis-of double},
force-zero? = false,
||-- force-range? = true,
||-- forced-min = 80,
||-- forced-max = 200,
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"}
},
{LineLayer
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"},
x-axis-data = {ChartDataSeries records, "Region"}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
chart
}
| |
| Example:
Using the pastel color palette |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 10cm,
height = 6cm,
color-palette = pastel-chart-color-palette,
left-axis = {new {NumericDataSeriesAxis-of double},
force-zero? = false,
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"}
},
{BarLayer
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"},
x-axis-data = {ChartDataSeries records, "Region"}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
chart
}
| |
| Example:
Using color association |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let bl:BarLayer =
{BarLayer
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"},
x-axis-data = {ChartDataSeries records, "Region"}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 10cm,
height = 6cm,
left-axis = {new {NumericDataSeriesAxis-of double},
force-zero? = false,
{ChartDataSeries records, "M1"},
{ChartDataSeries records, "M2"},
{ChartDataSeries records, "M3"}
},
bl
}
}
{bl.append-color-association
{DataSeriesColorPair
{ChartDataSeries records, "M1"},
"silver"
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
chart
}
| |
The Curl charts API enables you to supply procedures to modify the
way components of charts are generated. The API documentation
describes these "factories" in detail. The following list
summarizes some of the most important.
The following example uses
BarLayer.shape-factory to add
event handlers to the bars in a chart. The event handlers change
the bar color when you move the mouse pointer over the bar.
| Example:
Using shape-factory to add event handlers |
 |
{import * from CURL.GUI.CHARTS}
{import * from CURL.GRAPHICS.IMAGEFILTER}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let chart:LayeredChart =
{LayeredChart
width = 15cm,
height = 6cm,
bar-border-width = 0px,
bottom-axis =
{EnumeratedBarChartAxis
{ChartDataSeries records, "Region"}
},
{BarLayer
records,
"M1",
"M2",
"M3",
shape-factory =
{proc {layer:BarLayer,
rectangle:GRect,
record:Record,
record-index:int,
series-index:int,
border-width:any,
border-line-style:LineStyle
}:Shape
let constant stock-shape:Shape =
{BarLayer.default-shape-factory
layer,
rectangle,
record,
record-index,
series-index,
border-width,
border-line-style
}
let start-color:FillPattern = FillPattern.white
{stock-shape.add-event-handler
{on PointerEnter at s:Shape do
set start-color = s.color
set s.color = {brightness-adjust
start-color, 50%}
}
}
{stock-shape.add-event-handler
{on PointerLeave at s:Shape do
set s.color = start-color
}
}
{return stock-shape}
}
}
}
}
{value chart}
| |
You can add additional shape objects to a chart to supply
additional text, lines and so forth. The following example adds a
RectangleShape which acts as a level line, and a
TextShape which gives the value represented by the level line.
| Example:
Adding shapes to a chart |
 |
{import * from CURL.GUI.CHARTS}
{let records:RecordSet =
{RecordSet
{RecordFields
{RecordField
"Region",
domain = String
},
{RecordField "M1", caption = "January", domain = int},
{RecordField "M2", caption = "February", domain = int},
{RecordField "M3", caption = "March", domain = int}
},
{RecordData Region = "North", M1 = 100, M2 = 140, M3 = 130 },
{RecordData Region = "East", M1 = 110, M2 = 140, M3 = 170},
{RecordData Region = "South", M1 = 140, M2 = 100, M3 = 130},
{RecordData Region = "West", M1 = 160, M2 = 190, M3 = 140}
}
}
{let level-value:double = 110}
{let chart:LayeredChart =
{LayeredChart
width = 15cm,
height = 7cm,
{LineLayer
records,
"M1",
"M2",
"M3",
x-axis-data = {ChartDataSeries records, "Region"},
{on ChartLayoutChanged at ll:LineLayer do
let constant y:Distance =
{ll.chart.left-axis.get-position level-value}
{ll.add
{RectangleShape
{GRect 0m, {ll.chart.get-x-axis-length}, .5pt - y, .5pt + y},
color = "red",
{TextShape
"" & level-value,
valign = "bottom",
translation =
{Distance2d
0.9 * {ll.chart.get-x-axis-length},
y
}
}
}
}
}
}
}
}
{VBox
{RecordGrid height = 4cm, width = 10cm, record-source = records},
chart
}
| |
Copyright © 1998-2007 Sumisho Computer Systems Corp.
All rights reserved.
Curl, the Curl logo, Surge, and the Surge logo are trademarks of Sumisho
Computer Systems Corp. that are registered in the United States. Surge
Lab, the Surge Lab logo, and the Surge Lab Visual Layout Editor (VLE)
logo are trademarks of Sumisho Computer Systems Corp.