def compute_table(snowline): """ Compute the table with snowline height for each day of the year. @param snowline: Snowline definition. @type snowline: L{Snowline} @return: Table of 12*32 entries with snowline heights. @rtype: C{str} """ day_table = [None]*365 # Height at each day, starting at day 0 for dh in snowline.date_heights: doy = dh.name.reduce() if not isinstance(doy, expression.ConstantNumeric): raise generic.ScriptError('Day of year is not a compile-time constant', doy.pos) if doy.value < 1 or doy.value > 365: raise generic.ScriptError('Day of the year must be between 1 and 365', doy.pos) height = dh.value.reduce() if isinstance(height, expression.ConstantNumeric) and height.value < 0: raise generic.ScriptError('Height must be at least 0', height.pos) if dh.unit is None: if isinstance(height, expression.ConstantNumeric) and height.value > 255: raise generic.ScriptError('Height must be at most 255', height.pos) else: unit = dh.unit if unit.type != 'snowline': raise generic.ScriptError('Expected a snowline percentage ("snow%")', height.pos) if isinstance(height, expression.ConstantNumeric) and height.value > 100: raise generic.ScriptError('Height must be at most 100 snow%', height.pos) mul, div = unit.convert, 1 if isinstance(mul, tuple): mul, div = mul # Factor out common factors gcd = generic.greatest_common_divisor(mul, div) mul //= gcd div //= gcd if isinstance(height, (expression.ConstantNumeric, expression.ConstantFloat)): # Even if mul == div == 1, we have to round floats and adjust value height = expression.ConstantNumeric(int(float(height.value) * mul / div + 0.5), height.pos) elif mul != div: # Compute (value * mul + div/2) / div height = expression.BinOp(nmlop.MUL, height, expression.ConstantNumeric(mul, height.pos), height.pos) height = expression.BinOp(nmlop.ADD, height, expression.ConstantNumeric(int(div / 2), height.pos), height.pos) height = expression.BinOp(nmlop.DIV, height, expression.ConstantNumeric(div, height.pos), height.pos) # For 'linear' snow-line, only accept integer constants. if snowline.type != 'equal' and not isinstance(height, expression.ConstantNumeric): raise generic.ScriptError('Height is not a compile-time constant', height.pos) day_table[doy.value - 1] = height # Find first specified point. start = 0 while start < 365 and day_table[start] is None: start = start + 1 if start == 365: raise generic.ScriptError('No heights given for the snowline table', snowline.pos) first_point = start while True: # Find second point from start end = start + 1 if end == 365: end = 0 while end != first_point and day_table[end] is None: end = end + 1 if end == 365: end = 0 # Fill the days between start and end (exclusive both border values) startvalue = day_table[start] endvalue = day_table[end] unwrapped_end = end if end < start: unwrapped_end += 365 if snowline.type == 'equal': for day in range(start + 1, unwrapped_end): if day >= 365: day -= 365 day_table[day] = startvalue else: assert snowline.type == 'linear' if start != end: dhd = float(endvalue.value - startvalue.value) / float(unwrapped_end - start) else: assert startvalue.value == endvalue.value dhd = 0 for day in range(start + 1, unwrapped_end): uday = day if uday >= 365: uday -= 365 height = startvalue.value + int(round(dhd * (day - start))) day_table[uday] = expression.ConstantNumeric(height) if end == first_point: # All days done break start = end table = [None] * (12*32) for dy in range(365): today = datetime.date.fromordinal(dy + 1) if day_table[dy]: expr = day_table[dy].reduce() else: expr = None table[(today.month - 1) * 32 + today.day - 1] = expr for idx, d in enumerate(table): if d is None: table[idx] = table[idx - 1] #Second loop is needed because we need make sure the first item is also set. for idx, d in enumerate(table): if d is None: table[idx] = table[idx - 1] return table
def parse_property_value(prop_info, value, unit=None, size_bit=None): """ Parse a single property value / unit To determine the value that is to be used in nfo @param prop_info: A dictionary with property information @type prop_info: C{dict} @param value: Value of the property @type value: L{Expression} @param unit: Unit of the property value (e.g. km/h) @type unit: L{Unit} or C{None} @param size_bit: Bit that indicates the size of a multitile house Set iff the item is a house @type size_bit: C{int} or C{None} @return: List of values to actually use (in nfo) for the property @rtype: L{Expression} """ # Change value to use, except when the 'nfo' unit is used if unit is None or unit.type != 'nfo': # Save the original value to test conversion against it org_value = value # Multiply by property-specific conversion factor mul, div = 1, 1 if 'unit_conversion' in prop_info: mul = prop_info['unit_conversion'] if isinstance(mul, tuple): mul, div = mul # Divide by conversion factor specified by unit if unit is not None: if not 'unit_type' in prop_info or unit.type != prop_info[ 'unit_type']: raise generic.ScriptError("Invalid unit for property", value.pos) unit_mul, unit_div = unit.convert, 1 if isinstance(unit_mul, tuple): unit_mul, unit_div = unit_mul mul *= unit_div div *= unit_mul # Factor out common factors gcd = generic.greatest_common_divisor(mul, div) mul //= gcd div //= gcd if isinstance(value, (expression.ConstantNumeric, expression.ConstantFloat)): # Even if mul == div == 1, we have to round floats and adjust value value = expression.ConstantNumeric( int(float(value.value) * mul / div + 0.5), value.pos) if unit is not None and 'adjust_value' in prop_info: value = adjust_value(value, org_value, unit, prop_info['adjust_value']) elif mul != div: # Compute (value * mul + div/2) / div value = expression.BinOp( nmlop.MUL, value, expression.ConstantNumeric(mul, value.pos), value.pos) value = expression.BinOp( nmlop.ADD, value, expression.ConstantNumeric(int(div / 2), value.pos), value.pos) value = expression.BinOp( nmlop.DIV, value, expression.ConstantNumeric(div, value.pos), value.pos) elif isinstance(value, expression.ConstantFloat): # Round floats to ints value = expression.ConstantNumeric(int(value.value + 0.5), value.pos) # Apply value_function if it exists if 'value_function' in prop_info: value = prop_info['value_function'](value) # Make multitile houses work if size_bit is not None: num_ids = house_sizes[size_bit] assert 'multitile_function' in prop_info ret = prop_info['multitile_function'](value, num_ids, size_bit) assert len(ret) == num_ids return ret else: return [value]